wait()
* Causes the calling thread to wait until another thread calls the {@code
* notify()} or {@code notifyAll()} method of this object. This method can
* only be invoked by a thread which owns this object's monitor; see
* {@link #notify()} on how a thread can become the owner of a monitor.
* <p>
* A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
* <p>
* While the thread waits, it gives up ownership of this object's monitor.
* When it is notified (or interrupted), it re-acquires the monitor before
* it starts running.
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
* @throws InterruptedException
* if another thread interrupts this thread while it is waiting.
該方法的注釋說:使調用線程處于wait狀態(tài)迁筛,直到其他線程調用這個Object的notify或者notifyAll才會被喚醒榴啸。這個方法只能被擁有這個Object的Monitor才能被調用擦剑。一個正在wait的線程能夠被調用interrupt方法苏潜。
當一個線程處于Wait漱受,它放棄了它自己的Object Monitor瞻凤,當它被notified或者interrupted的時候伶唯,它會在它啟動之前重新請求這個monitor
noitify()
* Causes a thread which is waiting on this object's monitor (by means of
* calling one of the {@code wait()} methods) to be woken up. If more than one thread is waiting, one of them is chosen at the discretion of the VM. The chosen thread will not run immediately. The thread
* that called {@code notify()} has to release the object's monitor first.
* Also, the chosen thread still has to compete against other threads that
* try to synchronize on the same object.
* This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor by executing a synchronized method of that object; by executing the body of a {@code synchronized} statement that synchronizes on the object;by executing a synchronized static method if the object is of type {@code Class}.
使得一個正在等待這個Object的Monitor的線程被喚醒觉既。如果超過一個線程正在等待的話,那么就只有一個線程會被喚醒,而這個線程會由VM自己決定瞪讼。而這個被選擇的線程并不會立馬就進入run的狀態(tài)钧椰,調用了notify的線程會首先釋放這個Object的Monitor。并且符欠,被選擇的線程必須完成和其他線程完成對這個Object鎖的競爭嫡霞。這個方法只能被擁有這個Object的Monitor的線程調用。這個線程擁有這個Object的Monitor希柿,通過執(zhí)行一個同步方法或者一個同步的代碼塊來獲取這個對象的鎖诊沪,或者通過執(zhí)行這個對象的Class類來進行同步。
簡單來說曾撤,也就是在使用wait和notify的時候端姚,需要使用synchoronized代碼塊將對象進行Monitor操作,這個操作可以是一個同步代碼塊挤悉,也可以是一個同步的方法渐裸,也可以用一個class對象進行同步,總之装悲,在調用wait和notify的時候橄仆,必須要進行同步。并且在有多個線程處于wait狀態(tài)的時候衅斩,當調用notify的時候,只有一個線程會收到這個消息怠褐,如果是notifyAll的話畏梆,所有的線程都會進行競爭,最后也只會有一個線程能夠獲取到資源奈懒,但是它也不會立馬進行到run的狀態(tài)奠涌,而是進入就緒的狀態(tài),等待時間片到它的時候磷杏,就可以執(zhí)行了溜畅。而這個線程的選擇是靠JVM來自主決定的。
下面舉一個例子:
-
錯誤的例子:使用wait和noitify的時候沒有加同步代碼塊
public class Test { public static void main(String[] args) { Object lock = new Object(); ThreadA threadA = new ThreadA(lock); ThreadB threadB = new ThreadB(lock); threadA.start(); threadB.start(); } private static class ThreadA extends Thread { Object obj; public ThreadA(Object lock) { obj = lock; setName("ThreadA"); } @Override public void run() { super.run(); System.out.println("Start Wait:" + Thread.currentThread().getName()); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Wait End:" + Thread.currentThread().getName()); } } private static class ThreadB extends Thread { Object obj; public ThreadB(Object lock) { obj = lock; setName("ThreadB"); } @Override public void run() { super.run(); System.out.println("ThreadB Start:" + Thread.currentThread().getName()); try { Thread.sleep(5000L); } catch (InterruptedException e) { } System.out.println("After ThreadB Sleep 5S"); obj.notify(); System.out.println("ThreadB notify:" + Thread.currentThread().getName()); } } }
運行后的結果為:
-
正確使用wait notify的例子:
public class Test { public static void main(String[] args) { Object lock = new Object(); ThreadA threadA = new ThreadA(lock); ThreadB threadB = new ThreadB(lock); threadA.start(); threadB.start(); } private static class ThreadA extends Thread { Object obj; public ThreadA(Object lock) { obj = lock; setName("ThreadA"); } @Override public void run() { super.run(); System.out.println("Start Wait:" + Thread.currentThread().getName()); try { synchronized (obj) { obj.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Wait End:" + Thread.currentThread().getName()); } } private static class ThreadB extends Thread { Object obj; public ThreadB(Object lock) { obj = lock; setName("ThreadB"); } @Override public void run() { super.run(); System.out.println("ThreadB Start:" + Thread.currentThread().getName()); try { Thread.sleep(5000L); } catch (InterruptedException e) { } System.out.println("After ThreadB Sleep 5S"); synchronized (obj) { obj.notify(); } System.out.println("ThreadB notify:" + Thread.currentThread().getName()); } } }
運行結果:
從正確的結果可以看出极祸,在ThreadA和ThreadB同時啟動的時候慈格,ThreadB先運行,然后進入了Sleep遥金,后ThreadA運行浴捆,打印出了StartWait,然后處于wait狀態(tài)稿械,等到ThreadB從Sleep5秒醒來后选泻,調用object.notify,并且打印ThreadB notify,于是通知ThreadA页眯,接著ThreadA獲取到了Object的Monitor之后梯捕,結束運行。
2.錯誤的例子
讓B線程先啟動窝撵,并且在B線程執(zhí)行完之后傀顾,再繼續(xù)執(zhí)行主線程,之后再啟動A線程忿族,此時A線程中會調用wait方法锣笨,這時候線程A一直在等待notify而導致程序無法正常結束。
public class Test {
public static void main(String[] args) {
Object lock = new Object();
ThreadA threadA = new ThreadA(lock);
ThreadB threadB = new ThreadB(lock);
threadB.start();
try {
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
private static class ThreadA extends Thread {
Object obj;
public ThreadA(Object lock) {
obj = lock;
setName("ThreadA");
}
@Override
public void run() {
super.run();
System.out.println("Start Wait:" + Thread.currentThread().getName());
try {
synchronized (obj) {
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Wait End:" + Thread.currentThread().getName());
}
}
private static class ThreadB extends Thread {
Object obj;
public ThreadB(Object lock) {
obj = lock;
setName("ThreadB");
}
@Override
public void run() {
super.run();
System.out.println("ThreadB Start:" + Thread.currentThread().getName());
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
}
System.out.println("After ThreadB Sleep 5S");
synchronized (obj) {
obj.notify();
}
System.out.println("ThreadB notify:" + Thread.currentThread().getName());
}
}
}