標籤:多線程 並發 java 安全執行緒 thread
package com.lipeng;public class LoopDemo {/** * 線程A迴圈10次,然後線程B迴圈100次,然後A再迴圈10次,然後B再迴圈100次。如此迴圈50次。 * lipeng * 2015-4-10 * @param args */public static void main(String[] args) {MyTask task=new MyTask();RunA runA=new RunA(task);RunB runB=new RunB(task);new Thread(runA,"A").start(); //每個線程的start方法只能運行一次,但Run方法在不同的線程中可以運行多次。new Thread(runB,"B").start();}}
class RunA implements Runnable{private MyTask task;public RunA(MyTask task){this.task=task;}@Overridepublic void run() {try {for(int i=0;i<50;i++){task.A();}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
class RunB implements Runnable{private MyTask task;public RunB(MyTask task){this.task=task;}@Overridepublic void run() {try {for(int i=0;i<50;i++){task.B();}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
class MyTask{private boolean aGo=true;//切換開關,當true時A執行,false時B執行,執行完後切換條件public synchronized void A()throws Exception{while(!aGo) //為何不用if???{//A 不能運行,等待this.wait();}for(int i=0;i<10;i++){System.out.println("AAAAA--------------"+i);}//運行完之後,將aGo切換為FALSE,讓B運行aGo=false;this.notify();//喚醒一個正在等待的線程 }public synchronized void B()throws Exception{while(aGo){//B 不能運行,等待this.wait();}for(int i=0;i<100;i++){System.out.println("B--------------"+i);}//運行完之後,將aGo切換為true,讓A運行aGo=true;this.notify();//喚醒一個正在等待的線程 }}
說明:
1. 如果有大於兩個多線程在執行,最好用notifyAll,為什嗎?
例如,如果本例中多起幾個線程,如果此時有A1,B1線程在wait,某個A線程執行完,將aGo置為false,並喚醒正在等待鎖的其中一個線程,A1或B1,如果此時正好A1線程搶到了CPU,則A1運行,判斷方法A1進入wait狀態,而B1由於沒有被喚醒,之後繼續等待,那麼到最後,A1和B1都處於wait狀態,造成了死結。
而如果改為notifyAll的話,A1和B1都被喚醒,搶到鎖的線程先執行,如果A1搶到的話,繼續等待,然後B1繼續執行,因為此時aGo=false,所以B1執行,將aGo=true,A1繼續執行完畢。
結論:只要A和B啟動的線程的個數不同,就可能造成死結的情況。
2.判斷aGo的地方為什麼要使用while,if不行嗎?在wait方法說明中,也推薦使用while,因為在某些特定的情況下,線程有可能被假喚醒,使用while會迴圈檢測更穩妥。在本例中,A和B應該是交替啟動並執行,但是如果多啟動幾個線程,如果有A1線程在等待(wait)狀態,而A2線程執行完畢,aGo設定為false,喚醒正在等待的線程(可能是A也可能是B),如果是A被喚醒的話,如果用了if而不是while,那麼A執行,這樣A就連續執行了兩遍,不符合我們的設計要求,但如果換用while的話,即使A被喚醒,他會再次判斷aGo的值,發現是false,則繼續等待。直到B線程將其喚醒。
Java多線程與並發應用-(4)-傳統線程通訊技術試題