Java的wait(), notify()和notifyAll()應用心得。本站提示廣大學習愛好者:(Java的wait(), notify()和notifyAll()應用心得)文章只能為提供參考,不一定能成為您想要的結果。以下是Java的wait(), notify()和notifyAll()應用心得正文
wait(),notify()和notifyAll()都是java.lang.Object的辦法:
wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify(): Wakes up a single thread that is waiting on this object's monitor.
notifyAll(): Wakes up all threads that are waiting on this object's monitor.
這三個辦法,都是Java說話供給的完成線程間壅塞(Blocking)和掌握過程內調劑(inter-process communication)的底層機制。在說明若何應用前,先解釋一下兩點:
1. 正如Java內任何對象都能成為鎖(Lock)一樣,任何對象也都能成為前提隊列(Condition queue)。而這個對象裡的wait(), notify()和notifyAll()則是這個前提隊列的固有(intrinsic)的辦法。
2. 一個對象的固有鎖和它的固有前提隊列是相干的,為了挪用對象X內前提隊列的辦法,你必需取得對象X的鎖。這是由於期待狀況前提的機制和包管狀況持續性的機制是慎密的聯合在一路的。
(An object's intrinsic lock and its intrinsic condition queue are related: in order to call any of the condition queue methods on object X, you must hold the lock on X. This is because the mechanism for waiting for state-based conditions is necessarily tightly bound to the mechanism fo preserving state consistency)
依據上述兩點,在挪用wait(), notify()或notifyAll()的時刻,必需先取得鎖,且狀況變量須由該鎖掩護,而固有鎖對象與固有前提隊列對象又是統一個對象。也就是說,要在某個對象上履行wait,notify,先必需鎖定該對象,而對應的狀況變量也是由該對象鎖掩護的。
曉得怎樣應用後,我們來問上面的成績:
1. 履行wait, notify時,不取得鎖會若何?
請看代碼:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
obj.wait();
obj.notifyAll();
}
履行以上代碼,會拋出java.lang.IllegalMonitorStateException的異常。
2. 履行wait, notify時,不取得該對象的鎖會若何?
請看代碼:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Object lock = new Object();
synchronized (lock) {
obj.wait();
obj.notifyAll();
}
}
履行代碼,異樣會拋出java.lang.IllegalMonitorStateException的異常。
3. 為何在履行wait, notify時,必需取得該對象的鎖?
這是由於,假如沒有鎖,wait和notify有能夠會發生競態前提(Race Condition)。斟酌以下臨盆者和花費者的情形:
1.1臨盆者檢討前提(如緩存滿了)-> 1.2臨盆者必需期待
2.1花費者花費了一個單元的緩存 -> 2.2從新設置了前提(如緩存沒滿) -> 2.3挪用notifyAll()叫醒臨盆者
我們願望的次序是: 1.1->1.2->2.1->2.2->2.3
但在多線程情形下,次序有能夠是 1.1->2.1->2.2->2.3->1.2。也就是說,在臨盆者還沒wait之前,花費者就曾經notifyAll了,如許的話,臨盆者會一向等下去。
所以,要處理這個成績,必需在wait和notifyAll的時刻,取得該對象的鎖,以包管同步。
請看以下應用wait,notify完成的一個臨盆者、一個花費者和一個單元的緩存的簡略模子:
public class QueueBuffer {
int n;
boolean valueSet = false;
synchronized int get() {
if (!valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
System.out.println("Got: " + n);
valueSet = false;
notify();
return n;
}
synchronized void put(int n) {
if (valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
this.n = n;
valueSet = true;
System.out.println("Put: " + n);
notify();
}
}
public class Producer implements Runnable {
private QueueBuffer q;
Producer(QueueBuffer q) {
this.q = q;
new Thread(this, "Producer").start();
}
public void run() {
int i = 0;
while (true) {
q.put(i++);
}
}
}
public class Consumer implements Runnable {
private QueueBuffer q;
Consumer(QueueBuffer q) {
this.q = q;
new Thread(this, "Consumer").start();
}
public void run() {
while (true) {
q.get();
}
}
}
public class Main {
public static void main(String[] args) {
QueueBuffer q = new QueueBuffer();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}
所以,JVM經由過程在履行的時刻拋出IllegalMonitorStateException的異常,來確保wait, notify時,取得了對象的鎖,從而清除隱蔽的Race Condition。
最初來看看一道題:寫一個多線程法式,瓜代輸入1,2,1,2,1,2......
應用wait, notify處理:
public class OutputThread implements Runnable {
private int num;
private Object lock;
public OutputThread(int num, Object lock) {
super();
this.num = num;
this.lock = lock;
}
public void run() {
try {
while(true){
synchronized(lock){
lock.notifyAll();
lock.wait();
System.out.println(num);
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args){
final Object lock = new Object();
Thread thread1 = new Thread(new OutputThread(1,lock));
Thread thread2 = new Thread(new OutputThread(2, lock));
thread1.start();
thread2.start();
}
}