更新時間:2020-11-24 17:33:46 來源:動力節點 瀏覽1272次
為了解決競爭條件帶來的問題,我們可以對資源上鎖。多個線程共同讀寫的資源稱為共享資源,也叫臨界資源。涉及操作臨界資源的代碼區域稱為臨界區(Critical Section)。同一時刻,只能有一個線程進入臨界區。我們把這種情況稱為互斥,即不允許多個線程同時對共享資源進行操作,在同一時間只能被一個線程所占有的鎖稱之為Java多線程互斥鎖。
互斥鎖在java中的實現就是 ReetranLock , 在訪問一個同步資源時,它的對象需要通過方法 tryLock() 獲得這個鎖,如果失敗,返回 false,成功返回true。根據返回的信息來判斷是否要訪問這個被同步的資源。ReentrantLock 互斥鎖是可重入鎖,即某一線程可多次獲得該鎖。
進入臨界區前,需要先獲得互斥鎖。如果已經有線程正在使用資源,那么需要一直等待,直到其它線程歸還互斥鎖。
操作完共享資源之后,即退出臨界區時,需要歸還互斥鎖,以便其它等待使用該資源的線程能夠進入臨界區。
偽代碼示例:
wait(lock); //獲得互斥鎖
{
臨界區,操作共享資源
}
signal(lock); //歸還互斥鎖
Java 中可以使用 ReentrantLock 對臨界區上鎖,防止多個線程同時進入臨界區:
private static Lock bufferLock = new ReentrantLock();
public static void print(String msg) {
bufferLock.lock();
//臨界區,操作臨界資源 globalBuffer
bufferLock.unlock();
}
這里我們只需要在臨界區前使用 lock() 上鎖,在臨界區后使用 unlock() 解鎖即可,java.util.concurrent 幫我們實現了臨界區前判斷鎖狀態的工作,會自己決定是阻塞還是進入臨界區。
synchronized 關鍵字
java 為我們提供了更加簡便的方式,用于實現臨界區的互斥。
例如,我們可以為操作共享資源的函數加上 synchronized 關鍵字:
public synchronized void myFunction() {
//操作共享資源 A
}
通過這種方式,能夠確保同一時刻最多只有一個線程在執行該函數。如果資源 A 只在該函數中讀寫,那么可以保證資源 A 不會出現被多個線程同時讀寫的情況。
但是,如果在其它函數中也對共享資源 A 進行操作,那么就不能使用這種方式來實現資源的使用互斥。因為即使這些函數都聲明為 synchronized,也只是說明同一時刻不能有多個線程執行同一個函數,但允許多個線程同時執行不同的函數,而這些函數都在操作同一個資源 A。
下面我們給出另一種方法來實現資源使用的互斥鎖。
synchronized 代碼塊
通過聲明函數為 synchronized 的方式,只能實現函數體的互斥。要確保資源使用的互斥,即同一時刻只能有一個線程使用該資源,可以將操作資源 A 的語句放入 synchronized 代碼塊:
public void function1() {
......
synchronized (A) {
//操作資源 A
}
......
}
public void function2() {
......
synchronized (A) {
//操作資源 A
}
......
}
這樣,對于資源 A 來說,同一時刻,只能有一個對應的 synchronized 代碼塊執行。因此,無論是在哪個地方使用資源 A,都不會出現多個線程競爭該資源的情況。
由ReentrantLock 的構造函數可見,在實例化 ReentrantLock 的時候我們可以選擇實例化一個公平鎖或非公平鎖,而默認會構造一個非公平鎖。公平鎖與非公平鎖區別在于競爭鎖時的有序與否。Java多線程互斥鎖ReentrantLock是通過繼承接口Lock而實現的,類似的還有繼承 ReadWriteLock 實現的 ReentrantReadWriteLock(讀寫鎖),對此,在本站的Java多線程教程中有進一步的講解。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習