我應該標記一下的java同步知識

synchronized

 synchronized使用起來非常的方便,但是方便不等於簡單,裏面涉及的知識點還是挺多的,這裏簡單記錄一點筆記。

首先記錄一下我所認識的鎖,任何對象在頭信息裏都有一個鎖標記,類也是特殊的對象(class對象),同一時刻只能有一個線程能持有這個鎖,當用synchronized時,線程會獲取鎖,別的線程想要獲取這個鎖時只能等待這個線程釋放鎖。

這兩天看了很多人的文章,講的都大同小異,說到synchronized的用法,有的說三種,有的說四種,有的說五種,也不知道官方是怎麼定義的,我就把所有的都羅列一下:

鎖代碼塊 synchronized(this) 對象鎖
synchronized(object) 對象鎖
synchronized(class) 類鎖
鎖方法 synchronized void method() 對象鎖
static synchronized void method() 類鎖

同一個對象鎖/類鎖,同一時刻只能被一個線程獲取。對於小白玩家,這些就是大概了,再多的瞭解可以看《深入理解Java虛擬機》、《Java編程思想》等書,https://www.cnblogs.com/zaizhoumo/p/7700161.html 這篇博客也不錯。

 

wait、notify、notifyAll

這三個一般都放在一塊兒說,因爲都是在synchronized的同步塊中使用的。

鎖的管理是通過Monitor來完成的,Monitor維護着WaitSet和EntryList、owner。WaitSet裏的線程必須被notify/notifyAll喚醒,才能進入EntryList,EntryList中的線程準備爭奪鎖並等待鎖被釋放,owner是當前獲得鎖的線程,owner的線程在wait()方法之後會進入WaitSet。所以這三個狀態是互相轉換的。

notify和notifyAll的區別從字面可以理解,notify是隻喚醒一個WaitSet中的線程進入EntryList,notifyAll是喚醒全部WaitSet中的線程進入EntryList。假設WaitSet中有5個線程等待被喚醒,notify之後,WaitSet中還剩4個線程,被喚醒的線程參與鎖的競爭;notifyAll之後,WaitSet中的線程全部被喚醒,參與鎖的競爭。

小tips:永遠在循環中調用wait()

wait之後的線程被誰喚醒是不可知的,假設一個場景:“多線程生產,多線程消費”。

synchronized(this) {
    if (empty) {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    remove();
    notifyAll();
}
synchronized(this) {
    while(empty) {//被喚醒後再次檢查條件
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    remove();
    notifyAll();
}

這裏有兩段代碼,一個是if判斷,一個是while循環判斷。消費者發現empty條件成立時會進入等待,因爲生產者和消費者都是多個線程,如果喚醒這個消費者線程的是別的消費者線程,那麼第一段代碼就會走到remove()方法,可能會出現意料之外的異常。而第二段代碼在線程被喚醒後還會再次判斷,保證了remove()的前置條件。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章