筆記:多線程并發(fā)編程(3)AQS、 syncionzerd 和volatie原理

AQS(隊列同步器AbstractQueuedSynchronizer):

AQS使用了模板方法 設計模式 實現(xiàn)AQS 需要繼承 AbstractQueuedSynchronizer。最少實現(xiàn)tryAcquire 與 tryRelease

 /**
     * The synchronization state.
        aqs里的關鍵成員變量 通過檢查與修改state狀態(tài)來實現(xiàn)同步狀態(tài)的更改
     */
    private volatile int state;

?getState():獲取當前同步狀態(tài)。
?setState(int newState):設置當前同步狀態(tài)。
?compareAndSetState(int expect,int update):使用CAS設置當前狀態(tài),該方法能夠保證狀態(tài)設置的原子性。

  • AbstractQueuedSynchronizer中的模板方法:

圖片1.png

這些模板方法同步器提供的模板方法基本上分為3類:獨占式獲取與釋放同步狀態(tài)、共享式獲取與釋放、同步狀態(tài)和查詢同步隊列中的等待線程情況。

  • 可重寫的方法


    aqs方法1.png

    aqs方法2.png
實現(xiàn):
  • 不可重入鎖
//實現(xiàn)獨占鎖 不可重入
public class SelfLock  implements Lock {
    private static Sync sync = new Sync();
    private static class Sync extends AbstractQueuedSynchronizer{

        protected  boolean isHeldExclusively(){return getState()==1;}

        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getState()==0){
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        ConditionObject newCondition(){
            return new ConditionObject();
        }
    }

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    @Override
    public boolean tryLock() {

        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long timeOut, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(timeOut));
    }



    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}
 //自實現(xiàn)鎖測試
public class TestMyLock {

    public void test() {
        final Lock lock = new SelfLock();
        class Worker extends Thread {

            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getName());
                try {
                    SleepTools.second(1);
                } finally {
                    lock.unlock();
                }
            }
        }
        // 啟動4個子線程
        for (int i = 0; i < 4; i++) {
            Worker w = new Worker();
            //w.setDaemon(true);
            w.start();
        }
        // 主線程每隔1秒換行
        for (int i = 0; i < 10; i++) {
            SleepTools.second(1);
            //System.out.println();
        }
    }

    public static void main(String[] args) {
        TestMyLock testMyLock = new TestMyLock();
        testMyLock.test();
    }
}



  • 可重入鎖
//實現(xiàn)可重入鎖
public class SelfReentrantLock implements Lock {
    private static Sync sync = new Sync();
    private static class Sync extends AbstractQueuedSynchronizer{

        protected  boolean isHeldExclusively(){return getState()==1;}

        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }else if(getExclusiveOwnerThread() == Thread.currentThread()){//先釋放鎖
                setState(getState()+1);
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getExclusiveOwnerThread() != Thread.currentThread()){
                throw new IllegalMonitorStateException();
            }
            if(getState()==0){
                throw new IllegalMonitorStateException();
            }
            //
            setState(getState()-1);
            if(getState()==0){
                setExclusiveOwnerThread(null);
            }
            return true;
        }

        ConditionObject newCondition(){
            return new ConditionObject();
        }
    }

    @Override
    public void lock() {
        System.out.println(Thread.currentThread().getName()+" ready get lock");
        sync.acquire(1);
        System.out.println(Thread.currentThread().getName()+" already got lock");
    }

    @Override
    public void unlock() {
        System.out.println(Thread.currentThread().getName()+" ready release lock");
        sync.release(1);
        System.out.println(Thread.currentThread().getName()+" already released lock");
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    @Override
    public boolean tryLock() {

        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long timeOut, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(timeOut));
    }



    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}
//使用了不可重入鎖
public class UseReenTrantLock {
        private SelfLock reentrantLock=new SelfLock();
        private int count = 0;
        private synchronized void method(){
            reentrantLock.lock();
            try {
                System.err.println("當前線程"+Thread.currentThread().getName()+"進入。。。");
                count++;
                Thread.sleep(2000);
                System.out.println(count);
                System.err.println("當前線程"+Thread.currentThread().getName()+"退出。。。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally{
                reentrantLock.unlock();
            }
        }

        public static void main(String[] args){
            final UseReenTrantLock useReenTrantLock=new UseReenTrantLock();
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    useReenTrantLock.method();
                }
            },"t1");

            Thread t2=new Thread(new Runnable() {
                @Override
                public void run() {
                    useReenTrantLock.method();
                }
            },"t2");
            t1.start();
            t2.start();

        }
}
//使用可重入鎖
public class TestReenterSelfLock {

    static final Lock lock = new SelfReentrantLock();

    public void reenter(int x){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+":遞歸層級:"+x);
            int y = x - 1;
            if (y==0) return;
            else{
                reenter(y);
            }
        } finally {
            lock.unlock();
        }

    }

    public void test() {
        class Worker extends Thread {
            public void run() {
                System.out.println(Thread.currentThread().getName());
                SleepTools.second(1);
                reenter(3);
            }
        }
        // 啟動3個子線程
        for (int i = 0; i < 3; i++) {
            Worker w = new Worker();
            w.start();
        }
        // 主線程每隔1秒換行
        for (int i = 0; i < 100; i++) {
            SleepTools.second(1);
        }
    }

    public static void main(String[] args) {
        TestReenterSelfLock testMyLock = new TestReenterSelfLock();
        testMyLock.test();
    }
}



  • CLH隊列鎖:

CLH隊列鎖也是一種基于鏈表的可擴展、高性能、公平的自旋鎖,申請線程僅僅在本地變量上自旋,它不斷輪詢前驅的狀態(tài),假設發(fā)現(xiàn)前驅釋放了鎖就結束自旋。
每次要拿鎖時,先加入獲取隊列。一直不斷的自旋隊列的上一個節(jié)點是否獲得鎖了,自旋到一定次數(shù)后阻塞進入等候隊列
當一個線程需要獲取鎖時:
1.創(chuàng)建一個的QNode,將其中的locked設置為true表示需要獲取鎖,myPred表示對其前驅結點的引用


QNode.png

2.線程A對tail域調用getAndSet方法,使自己成為隊列的尾部,同時獲取一個指向其前驅結點的引用myPred


image.png

線程B需要獲得鎖,同樣的流程再來一遍
image.png

3.線程就在前驅結點的locked字段上旋轉,直到前驅結點釋放鎖(前驅節(jié)點的鎖值 locked == false)
4.當一個線程需要釋放鎖時,將當前結點的locked域設置為false,同時回收前驅結點
image.png

volatie原理

  • 在說volatie原理之前先來說下JMM(Java memory mode)

cpu高速緩存區(qū).png

image.png

在計算機系統(tǒng)中,寄存器劃是L0級緩存,接著依次是L1,L2,L3(接下來是內存,本地磁盤,遠程存儲)。越往上的緩存存儲空間越小,速度越快,成本也更高;越往下的存儲空間越大,速度更慢,成本也更低。從上至下,每一層都可以看做是更下一層的緩存,即:L0寄存器是L1一級緩存的緩存,L1是L2的緩存,依次類推;每一層的數(shù)據(jù)都是來至它的下一層,所以每一層的數(shù)據(jù)是下一層的數(shù)據(jù)的子集。

JMM模型圖.png

每個線程都會分配有一個工作內存,同時共享一個主內存。線程不能直接訪問主內存,只能操作工作內存。在讀取變量會從主內存讀取到自己的工作內存里進行操作。

  • 實現(xiàn)原理

volatile關鍵字修飾的變量會存在一個“l(fā)ock:”的前綴。
Lock前綴,Lock不是一種內存屏障,但是它能完成類似內存屏障的功能。Lock會對CPU總線和高速緩存加鎖,可以理解為CPU指令級的一種鎖。
同時該指令會將當前處理器緩存行的數(shù)據(jù)直接寫會到系統(tǒng)內存中,且這個寫回內存的操作會使在其他CPU里緩存了該地址的數(shù)據(jù)無效。
所以其他線程需要重新從主內存讀取

syncionzerd原理

Synchronized在JVM里的實現(xiàn)都是基于進入和退出Monitor對象來實現(xiàn)方法同步和代碼塊同步,雖然具體實現(xiàn)細節(jié)不一樣,但是都可以通過成對的MonitorEnter和MonitorExit指令來實現(xiàn)
對同步塊,MonitorEnter指令插入在同步代碼塊的開始位置,而monitorExit指令則插入在方法結束處和異常處,JVM保證每個MonitorEnter必須有對應的MonitorExit??偟膩碚f,當代碼執(zhí)行到該指令時,將會嘗試獲取該對象Monitor的所有權,即嘗試獲得該對象的鎖:
1、如果monitor的進入數(shù)為0,則該線程進入monitor,然后將進入數(shù)設置為1,該線程即為monitor的所有者。
2、如果線程已經(jīng)占有該monitor,只是重新進入,則進入monitor的進入數(shù)加1.
3.如果其他線程已經(jīng)占用了monitor,則該線程進入阻塞狀態(tài),直到monitor的進入數(shù)為0,再重新嘗試獲取monitor的所有權。
對同步方法,從同步方法反編譯的結果來看,方法的同步并沒有通過指令monitorenter和monitorexit來實現(xiàn),相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。
JVM就是根據(jù)該標示符來實現(xiàn)方法的同步的:當方法被調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個monitor對象。
synchronized使用的鎖是存放在Java對象頭里面,Java對象的對象頭由 mark word 和 klass pointer 兩部分組成:
1)mark word存儲了同步狀態(tài)、標識、hashcode、GC狀態(tài)等等。
2)klass pointer存儲對象的類型指針,該指針指向它的類元數(shù)據(jù)

  • 鎖的狀態(tài)與優(yōu)化
    一共有四種狀態(tài),無鎖狀態(tài),偏向鎖狀態(tài),輕量級鎖狀態(tài)和重量級鎖狀態(tài),它會隨著競爭情況逐漸升級。鎖可以升級但不能降級,目的是為了提高獲得鎖和釋放鎖的效率。
    1.偏向鎖,顧名思義,它會偏向于第一個訪問鎖的線程,如果在運行過程中,同步鎖只有一個線程訪問,不存在多線程爭用的情況,則線程是不需要觸發(fā)同步的,減少加鎖/解鎖的一些CAS操作(比如等待隊列的一些CAS操作),這種情況下,就會給線程加一個偏向鎖。
    偏向鎖:是指線程在操作資源之前 直接檢查 上次 獲得鎖的對象是不是當前要操作的線程如果是直接操作資源
    2.輕量級鎖:存在多個進程競爭鎖時,不直接加鎖而是通過cas操作來看是否能夠拿到鎖 通常周期為1個上下文切換時間周期。(自適應自旋鎖) 在從偏向鎖切換到輕量級鎖 還會進行一次線程暫停所以工作線程 gc工作(stop thte world)
    3.輕量級鎖超過一個切換上下文周期還是拿不到鎖就會膨脹為重量級鎖 此時線程會進入阻塞

  • 不同狀態(tài)對象頭里記錄的不同信息:


    鎖對象頭信息
  • 鎖的升級過程


    無鎖->偏向鎖.png
最終升級為重量級鎖.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容