以前把thread基礎溫習了一遍,而後想經過作一些題目來加深本身的印象,恰好大四找工做面試時被問到一道題目:A-Z,1-26,使用多線程打印出A1B2C3D4E5......這種,當時本身好像回答的並很差,因而今天從新寫了一遍。面試
首先咱們須要新建兩個類,一個用來打印A-Z,一個用來打印1-26。這裏爲了省略,我只打印了前面幾個數字和字母。咱們一步一步來。多線程
public class PrintCharacter extends Thread { @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { System.out.println(str); } } }
public class PrintNumber extends Thread { @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { System.out.println(i); } } }
public static void main(String[] args) { PrintCharacter pc = new PrintCharacter(); PrintNumber pn = new PrintNumber(); pc.setPriority(10); pn.setPriority(1); pc.start(); pn.start(); }
這裏的代碼比較簡單,主線程開啓了兩個線程,並設置打印字母的線程的優先級爲最高(網上說設置優先級並不能必定保證會先執行,可是我這裏試驗過不少次都是先打印字母的)。經過代碼咱們看出不出意外結果應該是ABCDE12345這樣打印的。不符合題目。而後我想到了sleep()這個方法,他可讓線程休眠必定時間再進入就緒狀態,那麼理論上他是徹底能夠實現題目的效果的。ide
public class PrintCharacter extends Thread { @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { try { System.out.println(str); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class PrintNumber extends Thread { @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { try { System.out.println(i); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
這裏與上面代碼的區別就是每次循環打印以後我讓線程休眠0.5s,首先打印字母A和數字1,而後兩個線程休眠0.5s,而後繼續打印,直到循環結束。運行結果證實這種方法的確可行,可是這種方法只能說完成了題目要求的。他須要線程一直休眠。這種實現方法我的以爲能夠幫助咱們理解sleep()方法,可是並很差。這個時候我想到了另外的一種實現方法,那就是多線程中最經常使用的鎖,經過鎖來實現交替打印。在說這種方法以前咱們再次說明wait()方法和notify()方法。this
wait()方法和notify()方法的使用前提都須要拿到鎖,所以他們一般在同步代碼塊中使用。wait()方法會釋放當前線程的鎖,而後自身進入阻塞狀態。notify()方法會喚醒一個正在阻塞狀態的線程,而後他不會當即釋放鎖,而是等到執行完同步代碼塊中的內容,在釋放鎖。而後就是wait()方法和notify()方法的順序不能錯,即先執行wait()方法在執行notify()方法才能被喚醒。介紹完這兩個方法,而後咱們開始看代碼。spa
public class PrintCharacter extends Thread { private Object lock; public PrintCharacter(Object lock) { this.lock = lock; } @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { synchronized (lock) { try { lock.notify(); System.out.println(str); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public class PrintNumber extends Thread { private Object lock; public PrintNumber(Object lock) { this.lock = lock; } @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { synchronized (lock) { try { lock.notify(); System.out.println(i); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public static void main(String[] args) { Object lock = 0; PrintCharacter pc = new PrintCharacter(lock); PrintNumber pn = new PrintNumber(lock); pc.setPriority(10); pn.setPriority(1); pc.start(); pn.start(); }
首先,咱們分析一下代碼,這兩個線程都有一個lock對象,並且經過synchronized關鍵字保證這個對象不能同時被多個線程訪問。而後在run()方法中,首先喚醒一個正在阻塞狀態的線程,而後打印。而後讓釋放鎖並讓自身阻塞。主線程運行時執行兩個子線程,首先執行打印字母的線程,他執行notify()方法,由於當前沒有線程處於阻塞狀態,因此不起做用,而後打印A,以後釋放鎖,並讓自身阻塞。而後打印數字的線程得到鎖開始執行,他執行notify()方法喚醒打印字母的線程,而後打印1,而後讓自身阻塞,最後釋放鎖。這樣一直循環。直到循環結束。線程
這種方法看上去挺好的,可是實際上仍是有問題的,問題就是當兩個線程最後一次循環的時候打印數字線程會調用wait()方法令自身阻塞。而後致使程序沒法結束,,,這樣看起來問題好像更嚴重了。。。既然打印數字得線程最後會阻塞,那麼咱們將它得wait()方法改成wait(1000),這樣當最後一次它阻塞自身1s後,就會從新就如就緒狀態執行完run()方法。這樣程序就能正常退出了。code