等待與通知
在java中,Object類有方法Object.wait()/Object.wait(long)和Object.notify()/Object.notifyAll()可以實現等待和通知;
- Object.wait() 線程暫停,等待喚醒
- Object.wait(long) 線程暫停一段時間,等待喚醒,如果超時自動啓動
- Object.notify()喚醒被暫停的任意一個線程
- Object.notifyAll()喚醒被暫停的所有線程
注意:Object.wait()方法和Thread.sleep()方法有點相似。
Object.wait():釋放cpu資源,等待喚醒;
Thread.sleep():佔用cpu資源,休眠結束之後繼續執行;
實例代碼:
高仿生成消費的小測試:list的值等待消費,生產者每隔一秒鐘生產一個;
// 測試當count=10 的時候對count 賦值0
private static List<String> list = new LinkedList<>();
public static void main(String[] args) {
new Thread(()->{
while (true){
System.out.println("第一個線程獲取鎖ing");
synchronized (list){
while (list.isEmpty()){
System.out.println("第一個線程消費完成,等待生產者生產");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.remove(0);
}
}
}).start();
// 一秒鐘生成一個
for(int i=0;i<20;i++){
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (list){
System.out.println("生產一個");
list.add(i+"");
list.notify();
}
}
}
說明:wait方法和notify方法一定要synchronized包含,否則會報如下錯誤, 等待線程和通知線程必須調用同一個對象的wait方法和notify方法;
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at com.WaitTest.main(WaitTest.java:42)
內部實現原理
java虛擬機會爲每個對象維護一個入口集(Entry Set)用於存儲申請該對象內部鎖的線程。另外,java
虛擬機還會爲每個對象維護一個被等待集(Wait Set)的隊列,該隊列用戶存儲該對象的等待集合。
Object.wait()將當前線程暫停並釋放相應的內部鎖的同時會將當前線程的引用存入該方法所屬對象的等待集合中
(wait方法會一直卡住,沒有返回)。執行一個對象的notify方法會使該對象的任意一個線程被喚醒。被喚醒的線
程需要等待Object.wait()吧當前線程移除等待集合,接着Object.wait()方法返回。Object.wait()/notify()等待通知機
制的幾個關鍵步驟:當前線程加入等待集合、暫停當前線程、釋放鎖以及將喚醒後的線程從等待線程移除,都
是在Object.wait()方法中實現的。
Thread.join()說明
Thread.join/Thread.join(long millis)可以使當前線程等待目標線程結束之後再繼續執行。
join(long)是一個同步方法。它檢測到目標線程沒有結束會調用wait方法暫停當前線程,直到目標線程已經終止。
如下,如果不使用join則會很快的打印出來“主線程執行完畢”,但是使用了join方法會先打印“子線程執行完畢”再打印“主線程執行完畢”
public static void main(String[] args) {
Thread thread = new Thread(()->{
try {
Thread.sleep(5000l);
System.out.println("子線程執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程執行完畢");
}
2.java條件變量
Object.wait()/notify()無法解決,過早喚醒的問題,還有wait(long)方法無法區分等待超時還是被通知線程喚醒的問題;
java條件變量即可解決以上問題。
java.util.concurrent.locks.Condition接口
public interface Condition {
//與Object.wait()用法一致
void await() throws InterruptedException;
//當前線程在接到信號之前一直處於等待狀態。 注:該方法不響應中斷
void awaitUninterruptibly();
//與Object.wait()方法一致
boolean await(long time, TimeUnit unit) throws InterruptedException;
//deadline 是等待的時刻
//false表示等待超時
boolean awaitUntil(Date deadline) throws InterruptedException;
//和awaitUntil類似,區別是等待時間,單位爲納秒
// 1秒==1000毫秒==1000000微妙==1000000000納秒
long awaitNanos(long nanosTimeout) throws InterruptedException;
//與Object.wait()方法效果一樣
void signal();
void signalAll();
}
Object.wait()/notify()是通過synchronized
配合使用,Condition
接口是在java.util.concurrent.locks.ReentrantLock
下使用。