Java鎖機制&wait方法&notify方法關系實例

前言

經常看到有sleep和wait的區別,大都千篇一律,都會談及到wait會釋放鎖,并且本線程將會進入wait狀態,只有回調了notify或者notifyAll方法之后才能夠喚醒,才能夠繼續執行。那么這一個過程究竟說的是什么?這么抽象的描述,能否有一個例子呢?本文主要講的就是這個問題。

問題實例化

這里還是用最為常見的銀行賬戶作為實例。假設有兩個賬戶,A賬戶資金100,B賬戶資金200,那么如果需要A轉給B120元,那么肯定是不行的,但是資金轉賬,欠債總是要還的,所以不能就此罷休吧(如果你有錢例外),所以這個操作只能暫時掛起。接下來,B可能欠了A一些人情(暫且這么認為),所以需要給A轉50塊錢,由于B有200元,足夠了,所以直接就轉了。這個時候A資金有了150,足夠轉給B120元了,所以前面掛起的操作需要繼續執行。(上述只是一個例子,不要轉牛角尖,說什么B轉A 50,還不如直接讓A轉給B 70)

語言描述

上面已經描述得很清楚了,這里主要是說明幾個關鍵點:
1)掛起,我們這里是將當前線程通過wait方法操作的
2)將前面掛起的操作恢復,需要在當前線程(非1)線程)notify或notifyAll

源碼分析

package hudson;


public class MainWork {
    
    public static void main(String[] args) {
        account = new int[]{100,200};
        MainWork mainWork = new MainWork();
        //需要注意的是子線程必須在主線程之前回調
        //原因在于,我們的主線程在回調transfer方法時由于
        //轉錢者資金不足會導致主線程進入wait狀態,需要等待
        //其他線程再次進入該對象拿到對象鎖,并通過回調notify或者notifyAll
        //方法來喚醒主線程繼續執行
        new Thread(){
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mainWork.transfer(1, 0, 10);
            };
        }.start();
        //由于賬戶0資金不足120,本方法回調會導致主線程進入wait狀態
        mainWork.transfer(0, 1, 120);
    }
    
    private static int[] account;
    public synchronized void transfer(int from,int to,int count){
        while(account[from]<count){
            try {
                System.out.println("對不起,資金不足,進入等待狀態");
                //進入等待,會釋放鎖。需要外界再次回調本方法(拿到本對象鎖),并
                //回調notify喚醒線程,以便查看是否資金是否充足
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("從"+from+"賬戶轉錢到"+to+"賬戶"+count+"元錢");
        account[from] -= count;
        account[to] += count;
        notifyAll();
    }

}

上述代碼非常簡單,但是需要有幾點注意:
1)子線程必須在主線程之前回調,因為主線程完成的是A轉B的第一次操作,由于A太窮,沒法支付,所以會導致進入wait狀態,即進入阻塞狀態。如果你把子線程放在后面,那么子線程是沒有機會執行的
2)在wait外部是用一個while循環包裹的,為什么不能用if呢?
原因是,如果使用if包裹,假設B轉給A的資金不足以使得A的資金超過120元,即不足以使得A后面有能夠還給B 120元,例如B轉給A 10元,那么A資金是110元,這個時候是不滿足A轉給B 120元的條件的,所以不應該往后面執行。啰嗦了這么一大堆,簡單地說就是在被其他線程喚醒之后,我們還需要判斷條件是否符合繼續執行

結果測試

視頻_2017-11-12_224942.gif

源代碼

https://github.com/HudsonAndroid/DataStructureAlgorithm/tree/master/java%E7%9F%A5%E8%AF%86/%E9%94%81%E6%9C%BA%E5%88%B6

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 相關概念 面向對象的三個特征 封裝,繼承,多態.這個應該是人人皆知.有時候也會加上抽象. 多態的好處 允許不同類對...
    東經315度閱讀 1,992評論 0 8
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 1.解決信號量丟失和假喚醒 public class MyWaitNotify3{ MonitorObject m...
    Q羅閱讀 912評論 0 1
  • 提鳥上場也中槍 到了11點,桃花一朵果然下線了,潘笑笑能夠猜出他在下線的最后一秒還在打字與她聊天,因為他有半句...
    亂世長安閱讀 289評論 0 1
  • 器皿加水果,所謂“一器一果”也! 蘋果6plus拍攝,MIX后期
    花落寂寂hjr閱讀 427評論 0 2