什麼是wait、notify和notifyAll
wait
、notify
、notifyAll
都是是Object的方法,任何一個繼承Object類的對象都可以調用- 所有的操作都是針對
某個特定的對象
來說的,比如對象的引用變了,那麼就相當於wait
和notify
作用在了兩個不同的對象上,會拋出IllegalMonitorStateException
- 這三個方法只有在當前線程獲取到對象的
Monitor
(可以理解爲鎖)以後纔可以調用 同一時間
,只有一個線程
可以獲得對象的Monitor
- 當前線程獲取對象的
Monitor
有三種方式- 通過執行這個對象的
synchronized
實例方法 - 通過執行以這個對象作爲同步鎖的
synchronized
方法體 - 對於
Class
類型的對象,執行一個該類型的synchronized
靜態方法
- 通過執行這個對象的
wait
調用後,會讓線程進入等待狀態,知道另外一個線程喚醒它,並釋放對象的Monitor
(釋放鎖)notify
調用後,會喚醒等待當前對象Monitor
的一個線程,具體哪一個不好說,由JVM控制notifyAll
調用後,會喚醒所有等待當前對象Monitor
的線程- 被喚醒的線程不會立刻運行,而是會等待持有對象
Monitor
的線程將Monitor
(鎖)釋放掉。在Monitor
被釋放掉以後,所有被喚醒的線程,將會重新競爭這個鎖,獲取到鎖的線程纔會執行wait
的下一條語句 - 官方文檔建議將
wait
放到一個循環中,因爲在多線程的環境下,共享對象的狀態隨時可能改變,所以通過while
循環的條件判斷,當滿足條件的情況下,再觸發wait
什麼時候釋放鎖?
由於等待鎖的線程只有在獲得這把鎖之後,才能恢復運行,所以讓持有鎖的線程在不需要鎖的時候及時釋放鎖是很重要的。
在以下情況下,持有鎖的線程會釋放鎖:
- 執行完同步代碼塊。
- 在執行同步代碼塊的過程中,遇到異常而導致線程終止。
- 在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進行對象的等待池。
除了以上情況外,只要持有鎖的線程還沒有執行完同步代碼塊,就不會釋放鎖。
因此在以下情況下,線程不會釋放鎖:
- 在執行同步代碼塊的過程中,執行了Thread.sleep()方法,當前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。
- 在執行同步代碼塊的過程中,執行了Thread.yield()方法,當前線程放棄CPU,但不會釋放鎖。
- 在執行同步代碼塊的過程中,其他線程執行了當前對象的suspend()方法,當前線程被暫停,但不會釋放鎖。但Thread類的suspend()方法已經被廢棄。
三種獲取Monitor
的方式
- 對象,鎖爲對象
public class WaitNotifyMonitorObjectDemo {
private static Object o = new Object();
private static boolean flag = true;
static final class A extends Thread {
@Override
public void run() {
synchronized (o) {
while (flag) {
try {
System.out.println("ready to wait");
o.wait();
System.out.println("i am back");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = true;
}
}
}
static final class B extends Thread {
@Override
public void run() {
synchronized (o) {
o.notify();
System.out.println("wake up o");
flag = false;
}
}
}
public static void main(String[] args) {
A a = new A();
a.start();
B b = new B();
b.start();
}
}
執行結果
ready to wait
i am back
ready to wait
- 同步方法,鎖爲方法所在對象
public class WaitNotifySyncMethodDemo {
private static boolean flag = true;
static class A {
synchronized void method1() {
while (flag) {
try {
System.out.println("ready to wait");
this.wait();
System.out.println("i am back");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = true;
}
synchronized void method2() {
this.notify();
}
}
public static void main(String[] args) {
final A a = new A();
new Thread(a::method1).start();
new Thread(a::method2).start();
}
}
執行結果
ready to wait
i am back
ready to wait
- 同步靜態方法,鎖爲類對象
public class WaitNotifySyncStaticMethodDemo {
private static boolean flag = true;
private synchronized static void method1() {
while (flag) {
try {
System.out.println("ready to wait");
WaitNotifySyncStaticMethodDemo.class.wait();
System.out.println("i am back");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = true;
}
private synchronized static void method2() {
WaitNotifySyncStaticMethodDemo.class.notify();
}
public static void main(String[] args) {
new Thread(WaitNotifySyncStaticMethodDemo::method1).start();
new Thread(WaitNotifySyncStaticMethodDemo::method2).start();
}
}
執行結果
ready to wait
i am back
ready to wait
notify和notifyAll的區別示例
- 待補充