JAVA鎖機制

synchronized

可重入鎖,看一段代碼
public static void main(String[] args) {
        B1 b1  = new B1();
        B1 b2  = new B1();

        Thread t1 = new Thread(()->{b1.say("p1");});
        t1.start();
        Thread t2 = new Thread(()->{b1.say("p2");});
        t2.start();
    }

    public synchronized void say(String p1){
        synchronized (this){
            System.out.println(p1);
            try {
                TimeUnit.MINUTES.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

編譯後的代碼java

經過jstack查看運行狀態: 優化

LockSupport

java還有3個鎖是經過LockSupoort實現的。爲何要說這個了,由於這個類支持線程的阻塞與釋放。 ReentrantLock, ReentrantReadWriteLock,StampedLock。 其中ReentrantLock,ReentrantReadWriteLock支持公平與非公平鎖。
公平:FIFO
非公平:隨機this

ReentrantLock

可重入鎖,我們先本身實現一個這樣的非公平鎖。線程

1 須要一個狀態,標記是否已有線程獲取到鎖了。0 未被鎖定 1 被鎖定
2 支持重入,須要知道這個鎖被哪一個線程持有,後續該線程再次獲取鎖時,狀態+1,表明該線程獲取了幾回鎖
3 2個線程同時獲取鎖,能夠通compareAndSet原子方法進行狀態改變,當某個線程獲取失敗,能夠返回失敗,也能夠阻塞,等待繼續獲取鎖,還能夠自旋繼續獲取鎖。非公平鎖挺適合用自旋的。
4 鎖的釋放,當某個線程釋放鎖時,因爲同一個時刻有且只能有一個線程釋放鎖,能夠將狀態變動,可重入鎖得進行減1釋放。
5 Condition實現,主要有await,signal,signalAll方法。
6 await方法,首先須要釋放鎖,這個很簡單,調用Lock的方法就能夠。而後調用LockSupport的阻塞方法,將當前線程阻塞掉。因爲須要支持signal,因此這裏須要一個隊列,來記錄順序,當signal的時候,取出第一個await進行喚醒。喚醒後,繼續經過自旋獲取鎖,拿到鎖後,就能夠return掉了。
7 signal方法,喚醒第一個await的線程。
8 signalAll方法,喚醒全部的await線程。

可重入公平鎖3d

1 稍微改動下,不用自旋的方法,咱們改爲用LockSupport的阻塞方法。
2 2個線程同時獲取鎖,先判斷是否能夠重入,重入鎖狀態+1,而後返回。不然建立一個隊列,若是不存在。而後狀態將該線程加入隊尾,再阻塞該線程。
3 徹底釋放鎖的時候,徹底釋放是指鎖狀態爲0,這個時侯,能夠從隊首拿出線程,消除阻塞,設置該線程獲取鎖,而後返回。

ReentrantReadWriteLock

可重入讀寫鎖。讀能夠屢次,寫只能一個線程獲取。讀寫互斥。 公平與非公平,能夠參照ReentrantLock。 這裏的讀鎖能夠理解爲共享鎖。code

1 獲取讀鎖時,先看是否有寫鎖。
2 獲取寫鎖時,先看是否有讀鎖。
3 獲取讀鎖,須要將每一個線程都存起來,並將每一個線程重入了幾回記錄起來。剩下的就好辦了。

StampedLock

jdk8 提供的鎖,只不過是對ReentrantReadWriteLock的優化。可是它不支持Condition。有一個優勢時,它是樂觀鎖。 何爲樂觀鎖:咱們能夠看一段源碼:blog

public long tryOptimisticRead() {
        long s;
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }

能夠看到這裏只是判斷是否有寫鎖。 對於大部分都是讀的場景,能夠這樣用。當你對這個數據使用完以後,須要校驗是否有寫鎖。隊列

相關文章
相關標籤/搜索