點選“終碼一生”,關注,置頂公衆号,每日技術幹貨,第一時間送達!
1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
2、wait()使目前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊裡使用 wait()、notify/notifyAll() 方法。
3、 由于 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明目前線程一定是擷取了鎖的。
當線程執行wait()方法時候,會釋放目前的鎖,然後讓出CPU,進入等待狀态。
隻有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處于等待狀态的線程,然後繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
也就是說,notify/notifyAll() 的執行隻是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。是以在程式設計中,盡量在使用了notify/notifyAll() 後立即退出臨界區,以喚醒其他線程讓其獲得鎖。
4、wait() 需要被try catch包圍,以便發生異常中斷也可以使wait等待的線程喚醒。
5、notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那麼B線程是無法被喚醒的。
6、notify 和 notifyAll的差別
notify方法隻喚醒一個等待(對象的)線程并使該線程開始執行。是以如果有多個線程等待一個對象,這個方法隻會喚醒其中一個線程,選擇哪個線程取決于作業系統對多線程管理的實作。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程将會第一個處理取決于作業系統的實作。如果目前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生産者-消費者裡面的使用,每次都需要喚醒所有的消費者或是生産者,以判斷程式是否可以繼續往下執行。
7、在多線程中要測試某個條件的變化,使用if 還是while?
要注意,notify喚醒沉睡的線程後,線程會接着上次的執行繼續往下執行。是以在進行條件判斷時候,可以先把 wait 語句忽略不計來進行考慮;顯然,要確定程式一定要執行,并且要保證程式直到滿足一定的條件再執行,要使用while進行等待,直到滿足條件才繼續往下執行。如下代碼:
public class K {
//狀态鎖
private Object lock;
//條件變量
private int now,need;
public void produce(int num){
//同步
synchronized (lock){
//目前有的不滿足需要,進行等待,直到滿足條件
while(now < need){
try {
//等待阻塞
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我被喚醒了!");
}
// 做其他的事情
}
}
}
顯然,隻有目前值滿足需要值的時候,線程才可以往下執行,是以,必須使用while 循環阻塞。注意,wait() 當被喚醒時候,隻是讓while循環繼續往下走.如果此處用if的話,意味着if繼續往下走,會跳出if語句塊。
8、實作生産者和消費者問題
什麼是生産者-消費者問題呢?
如上圖,假設有一個公共的容量有限的池子,有兩種人,一種是生産者,另一種是消費者。需要滿足如下條件:
1、生産者産生資源往池子裡添加,前提是池子沒有滿,如果池子滿了,則生産者暫停生産,直到自己的生成能放下池子。
2、消費者消耗池子裡的資源,前提是池子的資源不為空,否則消費者暫停消耗,進入等待直到池子裡有資源數滿足自己的需求。
- 倉庫類
/**
* 生産者和消費者的問題
* wait、notify/notifyAll() 實作
*/
public class Storage1 implements AbstractStorage {
//倉庫最大容量
private final int MAX_SIZE = 100;
//倉庫存儲的載體
private LinkedList list = new LinkedList();
//生産産品
public void produce(int num){
//同步
synchronized (list){
//倉庫剩餘的容量不足以存放即将要生産的數量,暫停生産
while(list.size()+num > MAX_SIZE){
System.out.println("【要生産的産品數量】:" + num + "\t【庫存量】:"
+ list.size() + "\t暫時不能執行生産任務!");
try {
//條件不滿足,生産阻塞
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i=0;i<num;i++){
list.add(new Object());
}
System.out.println("【已經生産産品數】:" + num + "\t【現倉儲量為】:" + list.size());
list.notifyAll();
}
}
//消費産品
public void consume(int num){
synchronized (list){
//不滿足消費條件
while(num > list.size()){
System.out.println("【要消費的産品數量】:" + num + "\t【庫存量】:"
+ list.size() + "\t暫時不能執行生産任務!");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消費條件滿足,開始消費
for(int i=0;i<num;i++){
list.remove();
}
System.out.println("【已經消費産品數】:" + num + "\t【現倉儲量為】:" + list.size());
list.notifyAll();
}
}
}
- 抽象倉庫類
public interface AbstractStorage {
void consume(int num);
void produce(int num);
}
- 生産者
public class Producer extends Thread{
//每次生産的數量
private int num ;
//所屬的倉庫
public AbstractStorage abstractStorage;
public Producer(AbstractStorage abstractStorage){
this.abstractStorage = abstractStorage;
}
public void setNum(int num){
this.num = num;
}
// 線程run函數
@Override
public void run()
{
produce(num);
}
// 調用倉庫Storage的生産函數
public void produce(int num)
{
abstractStorage.produce(num);
}
}
- 消費者
public class Consumer extends Thread{
// 每次消費的産品數量
private int num;
// 所在放置的倉庫
private AbstractStorage abstractStorage1;
// 構造函數,設定倉庫
public Consumer(AbstractStorage abstractStorage1)
{
this.abstractStorage1 = abstractStorage1;
}
// 線程run函數
public void run()
{
consume(num);
}
// 調用倉庫Storage的生産函數
public void consume(int num)
{
abstractStorage1.consume(num);
}
public void setNum(int num){
this.num = num;
}
}
- 測試
public class Test{
public static void main(String[] args) {
// 倉庫對象
AbstractStorage abstractStorage = new Storage1();
// 生産者對象
Producer p1 = new Producer(abstractStorage);
Producer p2 = new Producer(abstractStorage);
Producer p3 = new Producer(abstractStorage);
Producer p4 = new Producer(abstractStorage);
Producer p5 = new Producer(abstractStorage);
Producer p6 = new Producer(abstractStorage);
Producer p7 = new Producer(abstractStorage);
// 消費者對象
Consumer c1 = new Consumer(abstractStorage);
Consumer c2 = new Consumer(abstractStorage);
Consumer c3 = new Consumer(abstractStorage);
// 設定生産者産品生産數量
p1.setNum(10);
p2.setNum(10);
p3.setNum(10);
p4.setNum(10);
p5.setNum(10);
p6.setNum(10);
p7.setNum(80);
// 設定消費者産品消費數量
c1.setNum(50);
c2.setNum(20);
c3.setNum(30);
// 線程開始執行
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
p6.start();
p7.start();
}
}
- 輸出
【要消費的産品數量】:50 【庫存量】:0 暫時不能執行生産任務!
【要消費的産品數量】:20 【庫存量】:0 暫時不能執行生産任務!
【要消費的産品數量】:30 【庫存量】:0 暫時不能執行生産任務!
【已經生産産品數】:10 【現倉儲量為】:10
【要消費的産品數量】:30 【庫存量】:10 暫時不能執行生産任務!
【要消費的産品數量】:20 【庫存量】:10 暫時不能執行生産任務!
【要消費的産品數量】:50 【庫存量】:10 暫時不能執行生産任務!
【已經生産産品數】:10 【現倉儲量為】:20
【已經生産産品數】:10 【現倉儲量為】:30
【要消費的産品數量】:50 【庫存量】:30 暫時不能執行生産任務!
【已經消費産品數】:20 【現倉儲量為】:10
【要消費的産品數量】:30 【庫存量】:10 暫時不能執行生産任務!
【已經生産産品數】:10 【現倉儲量為】:20
【要消費的産品數量】:50 【庫存量】:20 暫時不能執行生産任務!
【要消費的産品數量】:30 【庫存量】:20 暫時不能執行生産任務!
【已經生産産品數】:10 【現倉儲量為】:30
【已經消費産品數】:30 【現倉儲量為】:0
【要消費的産品數量】:50 【庫存量】:0 暫時不能執行生産任務!
【已經生産産品數】:10 【現倉儲量為】:10
【要消費的産品數量】:50 【庫存量】:10 暫時不能執行生産任務!
【已經生産産品數】:80 【現倉儲量為】:90
【已經消費産品數】:50 【現倉儲量為】:40