多條線程之間有時需要數據交互,下面介紹5種線程間數據交互的方式,他們的使用處景各有不同。
PS:關于volatile的詳細介紹請移步至:Java并發編程的藝術(3)——volatile
這兩種方式都采取了同步機制實現多條線程間的數據通訊。與其說是“通訊”,倒不如說是“同享變量”來的恰當。當1個同享變量被volatile修飾 或 被同步塊包裹后,他們的讀寫操作都會直接操作同享內存,從而各個線程都能看到同享變量最新的值,也就是實現了內存的可見性。
這類方式能“傳遞”變量。當需要傳遞1些公用的變量時就能夠使用這類方式。如:傳遞boolean flag,用于表示狀態、傳遞1個存儲所有任務的隊列等。
用這類方式實現線程的開關控制。
// 用于控制線程當前的履行狀態 private volatile boolean running = false; // 開啟1條線程 Thread thread = new Thread(new Runnable(){ void run(){ // 開關 while(!running){
Thread.sleep(1000);
} // 履行線程任務 doSometing();
}
}).start(); // 開始履行 public void start(){
running = true;
}
等待/通知機制的實現由Java完成,我們只需調用Object類的幾個方法便可。
// 同享的狀態變量 boolean flag = false; // 線程1 Thread t1 = new Thread(new Runnable(){ public void run(){ while(!flag){
wait();
}
}
}).start(); // 線程2 Thread t2 = new Thread(new Runnable(){ public void run(){
flag = true;
notifyAll();
}
}).start();
上述例子thread1未加同步。當thread1履行到while那行后,判斷其狀態為true,此時若產生上下文切換,線程2開始履行,并1口氣履行完了;此時flag已是true,但是thread1繼續履行,遇到wait后便進入等待態;但此時已沒有線程能喚醒它了,因此就1直等待下去。
為何notify需要加鎖?且必須和wait使用同1把鎖?
首先,加鎖是為了保證同享變量的內存可見性,讓它產生修改后能直接寫入同享內存,好讓wait所處的線程立即看見。
其次,和wait使用同1把鎖是為了確保wait、notify之間的互斥,即:同1時刻,只能有其中1條線程履行。
為何必須使用同步塊的鎖對象調用wait函數?
首先,由于wait會釋放鎖,因此通過鎖對象調用wait就是告知wait釋放哪一個鎖。
其次,告知線程,你是在哪一個鎖對象上等待的,只有當該鎖對象調用notify時你才能被喚醒。
為何必須使用同步塊的鎖對象調用notify函數?
告知notify,只喚醒在該鎖對象上等待的線程。
等待/通知機制用于實現生產者和消費者模式。
synchronized(鎖A){
flag = true;// 或:list.add(xx); 鎖A.notify();
}
synchronized(鎖A){ // 不滿足條件 while(!flag){ // 或:list.isEmpty() 鎖A.wait();
} // doSometing…… }
在之前的生產者-消費者模式中,如果生產者沒有發出通知,那末消費者將永久等待下去。為了不這類情況,我們可以給消費者增加超時等待功能。該功能依托于wait(long)方法,只需在wait前的檢查條件中增加超時標識位,實現以下:
public void get(long mills){
synchronized( list ){ // 不加超時功能 if ( mills <= 0 ) { while( list.isEmpty() ){ list.wait();
}
} // 添加超時功能 else {
boolean isTimeout = false; while(list.isEmpty() && isTimeout){ list.wait(mills);
isTimeout = true;
} // doSometing…… }
}
}
管道流用于在兩個線程之間進行字節流或字符流的傳遞。
步驟以下:
1. 在1條線程中分別創建輸入流和輸出流;
2. 將輸入流和輸出留連接起來;
3. 將輸入流和輸出流分外傳遞給兩條線程;
4. 調用read和write方法就能夠實現線程間通訊。
// 創建輸入流與輸出流對象 PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(); // 連接輸入輸出流 out.connect(in); // 創建寫線程 class WriteThread extends Thread{ private PipedWriter out; public WriteThread(PipedWriter out){ this.out = out;
} public void run(){
out.write("hello concurrent world!");
}
} // 創建讀線程 class ReaderThread extends Thread{ private PipedReader in; public ReaderThread(PipedReader in){ this.in = in;
} public void run(){
in.read();
}
} //
public static void main(String[] args){ // 開啟1條線程 Thread t = new Thread(new Runnable(){ public void run(){ // doSometing }
}).start(); // 調用join,等待t線程履行終了 try{
t.join();
}catch(InterruptedException e){ // 中斷處理…… }
}
上一篇 (1)版本控制工具之Git
下一篇 Linux sed編輯器