執行緒間的通訊wait與notify
wait()方法
wait()方法使得當前執行緒必須要等待,等到另外一個執行緒呼叫notify()或者notifyAll()方法。
當前的執行緒必須擁有當前物件的monitor,也即lock,就是鎖。
執行緒呼叫wait()方法,釋放它對鎖的擁有權,然後等待另外的執行緒來通知它(通知的方式是notify()或者notifyAll() 方法),這樣它才能重新獲得鎖的擁有權和恢復執行。
要確保呼叫wait()方法的時候擁有鎖,即,wait()方法的呼叫必須放在synchronized方法或synchronized塊中。
wait()與sleep()比較
當執行緒呼叫了wait()方法時,它會釋放掉物件的鎖。
另一個會導致執行緒暫停的方法:Thread.sleep(),它會導致執行緒睡眠指定的毫秒數,但執行緒在睡眠的過程中是不會釋放 掉物件的鎖的。
notify()方法
notify()方法會喚醒一個等待當前物件的鎖的執行緒。
如果多個執行緒在等待,它們中的一個將會選擇被喚醒。這種選擇是隨意的,和具體實現有關。(執行緒等待一個物件的鎖 是由於呼叫了wait方法中的一個)。
被喚醒的執行緒是不能被執行的,需要等到當前執行緒放棄這個物件的鎖。
下面看一個程式碼示例:
public class ListAdd1 { private volatile static List list = new ArrayList(); public void add(){ list.add("bjsxt"); } public int size(){ return list.size(); } public static void main(String[] args) { final ListAdd1 list1 = new ListAdd1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { for(int i = 0; i <10; i++){ list1.add(); System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素.."); Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true){ if(list1.size() == 5){ System.out.println("當前執行緒收到通知:" + Thread.currentThread().getName() + " list size = 5 執行緒停止.."); throw new RuntimeException(); } } } }, "t2"); t1.start(); t2.start(); } }
執行結果如下:
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
Exception in thread "t2" 當前執行緒收到通知:t2 list size = 5 執行緒停止..
java.lang.RuntimeException
at com.fyw.thread.ListAdd1$2.run(ListAdd1.java:42)
at java.lang.Thread.run(Unknown Source)
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
這種方式實現的弊端是T2執行緒需要一個死迴圈一直監聽著List的大小,如果要用wait/notify實現如下:
public class ListAdd2 {
private volatile static List list = new ArrayList();
public void add(){
list.add("bjsxt");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd2 list2 = new ListAdd2();
final Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
System.out.println("t1啟動..");
for(int i = 0; i <10; i++){
list2.add();
System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素..");
Thread.sleep(500);
if(list2.size() == 5){
System.out.println("已經發出通知..");
lock.notify();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("t2啟動..");
if(list2.size() != 5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("當前執行緒:" + Thread.currentThread().getName() + "收到通知執行緒停止..");
throw new RuntimeException();
}
}
}, "t2");
t2.start();
t1.start();
}
}
執行結果如下:
t2啟動..
t1啟動..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
已經發出通知..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t2收到通知執行緒停止..
Exception in thread "t2" java.lang.RuntimeException
at com.fyw.thread.ListAdd2$2.run(ListAdd2.java:60)
at java.lang.Thread.run(Unknown Source)
這種實現方式也有一個問題,就是t2需要等到t1執行緒全部執行完成之後才能停止,不能做到實時性,我們可以使用CountDownLatch實現,程式碼如下:
public class ListAdd3 {
private volatile static List list = new ArrayList();
public void add(){
list.add("bjsxt");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd3 list2 = new ListAdd3();
// 倒計時的次數1
final CountDownLatch coundDownLatch = new CountDownLatch(1);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t1啟動..");
for(int i = 0; i <10; i++){
list2.add();
System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素..");
Thread.sleep(500);
if(list2.size() == 5){
System.out.println("已經發出通知..");
coundDownLatch.countDown();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t2啟動..");
if(list2.size() != 5){
try {
coundDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("當前執行緒:" + Thread.currentThread().getName() + "收到通知執行緒停止..");
throw new RuntimeException();
}
}, "t2");
t2.start();
t1.start();
}
}
執行結果如下:
t2啟動..
t1啟動..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
已經發出通知..
當前執行緒:t1添加了一個元素..
當前執行緒:t2收到通知執行緒停止..
Exception in thread "t2" java.lang.RuntimeException
at com.fyw.thread.ListAdd3$2.run(ListAdd3.java:60)
at java.lang.Thread.run(Unknown Source)
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..