簡介
wait()
、notify()
方法可用於控制線程的生命週期
詳解
1、wait() 不帶參數
在 當前線程 中調用 A對象 的
wait()
方法,此時 當前線程 會進入 等待狀態,此時 當前線程 會釋放所持有的 鎖資源,等到 其它線程 調用 A對象 的notify()
或者notifyAll()
進行喚醒,當前線程 纔有可能重新拿到 鎖資源 並繼續執行。
a、調用 wait() 與 notify() 的不是同一個對象-錯誤
/**
* 調用 wait()、notify() 的不是同一個對象
*/
public class WaitDetail01Main {
public static void main(String[] args) {
WaitDetail01 t1 = new WaitDetail01();
WaitDetail01 t2 = new WaitDetail01();
t1.setFlag(true);
t2.setFlag(false);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}
class WaitDetail01 extends Thread{
static Object o1 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (this){
try {
// 當前 this 爲 A線程對象
// 線程B對象調用 notify() 不會喚醒 線程A
// 一直等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
}
}else{
// B線程
synchronized (this){
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
// 當前 this 爲 線程B對象
this.notify();
}
}
}
}
解析:調用 wait()
和 notify()
的必須是 同一個對象
b、當前線程未持有鎖對象,去調用 wait()、notify()-錯誤
/**
* 當前線程未持有鎖,去調用 wait()、notify()
*/
public class WaitDetail02Main {
public static void main(String[] args) {
WaitDetail02 t1 = new WaitDetail02();
WaitDetail02 t2 = new WaitDetail02();
t1.setFlag(true);
t2.setFlag(false);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}
class WaitDetail02 extends Thread{
static Object o1 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
try {
// A線程
// 未持有 o1 鎖對象,調用 wait()
// 拋出 IllegalMonitorStateException
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
}
}else{
// B線程
// 未持有 o1 鎖對象,調用 notify()
// 拋出 IllegalMonitorStateException
o1.notify();
synchronized (o1){
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
}
}
}
}
解析:在當前線程中調用 wait()
、notify()
方法之前,必須保證當前線程 此時 已經持有了 鎖對象,否則拋出 IllegalMonitorStateException
2、wait() 帶參數
wait(long timeout)
帶一個參數:指定一個超時時間,在 當前線程 中調用 A對象 的wait(50)
方法,此時 當前線程 會進入 等待狀態,此時 當前線程 會釋放所持有的 鎖資源,等到 50毫秒 時間到了之後,自動被喚醒,纔有可能重新拿到 鎖資源 並繼續執行。
a、wait() 帶參數-正確
/**
* wait() 帶參數
*/
public class WaitDetail03Main {
public static void main(String[] args) {
WaitDetail03 t1 = new WaitDetail03();
WaitDetail03 t2 = new WaitDetail03();
t1.setFlag(true);
t2.setFlag(false);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}
class WaitDetail03 extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (o1){
try {
// 等待 50 毫秒後 自動喚醒
// 50 毫秒後,B線程已經執行完畢並釋放了鎖資源 o1
// 此時 線程A 被自動喚醒,並重新獲取鎖資源 o1
o1.wait(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
}
}else{
synchronized (o1){
System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
}
}
}
}
解析:wait()
帶參數的方法不需要滿足:wait()
與 notify()
的調用者是同一個對象,因爲它不會 顯示調用 notify()
;但還是必須滿足:在當前線程中調用 wait()
帶參數的方法之前,必須保證當前線程 此時 已經持有了 鎖對象,否則拋出 IllegalMonitorStateException
3、notify()
隨機喚醒某個因爲執行了
wait()
方法而在 等待區 等待的線程
4、notifyAll()
喚醒所有因爲執行了
wait()
方法而在 等待區 等待的線程