jvm多個線程間的通訊是通過 線程的鎖、條件陳述式、以及wait()、notify()/notifyAll組成。
下面來實現一個啟用多個線程來迴圈的輸出兩個不同的語句。
package com.app.thread;
import javax.swing.plaf.SliderUI;
/**
* 看出問題來
* @author Gordon
*
*/
public class LockDemo {
public static void main(String[] args) {
// System.out.println("lock");
final OutTurn ot = new OutTurn();
for(int j=0;j<100;j++){
new Thread(new Runnable() {
public void run() {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = 0; i <5; i++) {
ot.sub();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = 0; i < 5; i++) {
ot.main();
}
}
}).start();
}
}
}
class OutTurn {
private boolean isSub = true;
private int count=0;
public synchronized void sub() {
try {
while (!isSub) {
this.wait();
}
System.out.println("sub ---- "+count);
isSub=false;
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
public synchronized void main() {
try {
while(isSub){
this.wait();
}
System.out.println("main (((((((((((( "+count);
isSub=true;
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
}
不知能否看出問題,第一次寫的時候出現了問題,找了很久才找了出來,一直以來是沒有注意notify與notifyAll()的使用,在此釀成大錯,哎。。。
說明一下notify與notifyAll的區別:
以上sub和main方法都是用了鎖,所以說多個調用sub方法的線程和多個調用main方法的都會處於阻塞狀態,都會等待一個正在啟動並執行其他線程來喚醒他們,以上代碼使用了notify進行喚醒,notify只能喚醒一個線程,其他等待的線程仍然處於wait狀態,如果調用sub方法的線程執行完後,所有的線程都處於等待狀態,isSub=false了,這時喚醒的是一個sub方法調度線程,那麼while迴圈等於true,則該線程也會處於等待狀態,之後所有的線程處於等待狀態,沒有啟動並執行線程來喚醒他們,這時就產生了死結。如果使用notifyAll()來喚醒所有正在等待該鎖的線程,那麼所有的線程都會處於運行前的準備狀態,就是sub方法執行完後,喚醒了所有等待該鎖的狀態,那麼即使再次喚醒一個sub方法調度線程,那麼該線程再次處於等待狀態後,還有其他的線程可以獲得該鎖,進入運行狀態。所以notify方法很容易引起死結,除非你根據自己的程式設計,確定不會引起死結,notifyAll則是線程的安全喚醒方法。
言歸正傳,以上代碼 只需要將sub和main方法中的參數改成this.notifyAll()即可。