日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

學習JUC源碼(2)——自定義同步組件

 丹楓無跡 2021-06-22

前言

  在之前的博文(學習JUC源碼(1)——AQS同步隊列(源碼分析結(jié)合圖文理解))中,已經(jīng)介紹了AQS同步隊列的相關(guān)原理與概念,這里為了再加深理解ReentranLock等源碼,模仿構(gòu)造同步組件的基本模式,編寫不可重入的互斥鎖Mutex與指定共享線程數(shù)量的共享鎖。MySharedLock。

  主要參考資料《Java并發(fā)編程藝術(shù)》(有需要的小伙伴可以找我,我這里只有電子PDF)同時結(jié)合ReentranLock、AQS等源碼。


一、構(gòu)造同步組件的模式

叢概念方層面,在中,我們知道鎖與同步器的相關(guān)概念:

  • 同步器是實現(xiàn)鎖的關(guān)鍵,在鎖的實現(xiàn)中聚合同步器,利用同步器實現(xiàn)鎖的語義;

  • 鎖是面向使用者的,提供鎖交互的實現(xiàn);

  • 同步器是面向鎖的實現(xiàn)者,簡化了鎖的實現(xiàn)方式,屏蔽了同步狀態(tài)管理、線程排隊、等待/喚醒等底層操作。

從代碼層面,同步器是基于模板模式實現(xiàn)的,可以通過可重寫的方法中的隨便一個窺探:

  /** * 模板方法:
     *  protected關(guān)鍵字
     *  沒有任何實現(xiàn)
     * @param arg
     * @return */protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
    }

也就是需要進行以下幾步:

1)繼承同步器重寫指定方法(idea中extends AQS點擊快捷鍵ctrl+O即可顯示)

  • tryAcquire(int arg):獨占式獲取同步狀態(tài);

  • tryRelease(int arg):獨占式釋放同步狀態(tài);

  • tryAcquireShared(int arg):共享式獲取同步狀態(tài),返回大于0的值表示獲取成功,否則失敗

  • tryReleaseShared(int arg):共享式釋放鎖

  • isHeldExclusively():當前線程是否在獨占模式下被線程占用,一般該方法表示是否被當前線程占用

2)隨后將同步器組合在自定義同步組件的實現(xiàn)中,即定義內(nèi)部類Syn繼承AQS,在Syn中重寫AQS方法:

public class Sync extends AbstractQueuedSynchronizer{
        @Overrideprotected boolean tryAcquire(int arg) {final Thread current = Thread.currentThread();if (compareAndSetState(0, 1)) {// 獲取成功之后,當前線程是該鎖的持有者,不需要再可重入數(shù)                setExclusiveOwnerThread(current);return true;
            }return false;
        }

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

        @Overrideprotected boolean isHeldExclusively() {              return getState() == 1;
        }// 返回Condition,每個Condition都包含了一個隊列        Condition newCondition() {return new ConditionObject();
        }
    }

3)最后調(diào)用同步器提供的模板方法,即同步組件類實現(xiàn)Lock方法之后,在lock/unlock方法中調(diào)用內(nèi)部類Syn的方法acquire(int arg)等方法

public class Mutex implements Lock {
    
   ........
    @Overridepublic void lock() {
        sync.acquire(1);
    }
    @Overridepublic void unlock() {
        sync.release(1);
    }
    ........

}

具體請看下面的實踐部分

二、互斥不可重入鎖

 在我之前寫過的博文中(詳解Java鎖的升級與對比(1)——鎖的分類與細節(jié)(結(jié)合部分源碼))介紹可重入鎖與不可重入鎖的區(qū)別時,就寫到JUC中沒有不可重入鎖的具體實現(xiàn),但是可以類比,現(xiàn)在呢,我們可以做到實現(xiàn)了,具體看下面代碼,模式完全符合依賴Lock與AQS構(gòu)造同步組件模式。

(1)Mutex代碼實現(xiàn)(核心關(guān)鍵實現(xiàn)已經(jīng)在代碼中注釋)

public class Mutex implements Lock {private final Sync sync = new Sync();public class Sync extends AbstractQueuedSynchronizer{
        @Overrideprotected boolean tryAcquire(int arg) {final Thread current = Thread.currentThread();if (compareAndSetState(0, 1)) {// 獲取成功之后,當前線程是該鎖的持有者,不需要再可重入數(shù)                setExclusiveOwnerThread(current);return true;
            }return false;
        }

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

        @Overrideprotected boolean isHeldExclusively() {              return getState() == 1;
        }// 返回Condition,每個Condition都包含了一個隊列        Condition newCondition() {return new ConditionObject();
        }
    }


    @Overridepublic void lock() {
        sync.acquire(1);
    }
    @Overridepublic void unlock() {
        sync.release(1);
    }

    @Overridepublic void lockInterruptibly() throws InterruptedException {

    }

    @Overridepublic boolean tryLock() {return false;
    }

    @Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;
    }



    @Overridepublic Condition newCondition() {return null;
    }
}

其中核心代碼就是重寫的兩個方法:

  • tryAcquire(int arg)方法:主要是設(shè)置同獨占式更新同步狀態(tài),CAS實現(xiàn)state+1

  • tryRelease(int arg)方法:獨占式釋放同步狀態(tài),釋放鎖持有 

(2)測試Demo

public class MutexDemo {

    @Testpublic void test(){final Mutex lock = new Mutex();class Worker extends Thread {
            @Overridepublic void run() {// 一直不停在獲取鎖while (true) {
                    lock.lock();try {
                        System.out.println(Thread.currentThread().getName() +" hold lock, "+new Date());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println(Thread.currentThread().getName() +" release lock, "+new Date());
                    }
                }
            }

        }for (int i = 0; i < 10; i++) {
            Worker worker = new Worker();// 以守護進程運行,VM退出不影響運行,這里只是為了一個打印效果,去掉注釋一直打印worker.setDaemon(true);
            worker.start();
        }// 每隔一秒換行for (int j = 0; j < 10; j++) {try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println();
        }
    }
}

(3)運行結(jié)果

Thread-0 hold lock, Tue Dec 08 16:26:42 CST 2020Thread-0 release lock, Tue Dec 08 16:26:43 CST 2020Thread-1 hold lock, Tue Dec 08 16:26:43 CST 2020Thread-2 hold lock, Tue Dec 08 16:26:44 CST 2020Thread-1 release lock, Tue Dec 08 16:26:44 CST 2020Thread-3 hold lock, Tue Dec 08 16:26:45 CST 2020Thread-2 release lock, Tue Dec 08 16:26:45 CST 2020Thread-3 release lock, Tue Dec 08 16:26:46 CST 2020Thread-4 hold lock, Tue Dec 08 16:26:46 CST 2020Thread-4 release lock, Tue Dec 08 16:26:47 CST 2020Thread-6 hold lock, Tue Dec 08 16:26:47 CST 2020Thread-7 hold lock, Tue Dec 08 16:26:48 CST 2020Thread-6 release lock, Tue Dec 08 16:26:48 CST 2020Thread-7 release lock, Tue Dec 08 16:26:49 CST 2020Thread-5 hold lock, Tue Dec 08 16:26:49 CST 2020Thread-8 hold lock, Tue Dec 08 16:26:50 CST 2020Thread-5 release lock, Tue Dec 08 16:26:50 CST 2020Thread-8 release lock, Tue Dec 08 16:26:51 CST 2020Thread-9 hold lock, Tue Dec 08 16:26:51 CST 2020

(4)結(jié)果分析

互斥鎖的核心就是同一個同步狀態(tài)只能被一個線程持有,其它線程等待持有線程釋放才能競爭獲取。截圖一開始的運行結(jié)果分析:

Thread-0 hold lock, Tue Dec 08 16:26:42 CST 2020Thread-0 release lock, Tue Dec 08 16:26:43 CST 2020Thread-1 hold lock, Tue Dec 08 16:26:43 CST 2020Thread-2 hold lock, Tue Dec 08 16:26:44 CST 2020Thread-1 release lock, Tue Dec 08 16:26:44 CST 2020

10個線程不斷競爭鎖,一開始Thread-0在08 16:26:42獲取到鎖,持有鎖1秒后在釋放16:26:43時釋放,同時Thread-1立馬獲取到鎖,1秒后于16:26:44釋放鎖,同時Thread-2立馬獲取到了鎖......

根據(jù)輸出結(jié)果來說,完全符合Mutex作為互斥鎖這個功能:同一時刻只有一個線程持有鎖(同步狀態(tài)),其它線程等待釋放后才能獲取。

三、指定共享線程數(shù)目的共享鎖

(1)代碼實現(xiàn)(核心關(guān)鍵實現(xiàn)已經(jīng)在代碼中注釋)

public class MyShareLock implements Lock {// 可以看到共享等待隊列中的線程public Collection<Thread> getSharedQueuedThreads(){return syn.getSharedQueuedThreads();
    }private final Syn syn = new Syn(2);private static final class Syn extends AbstractQueuedSynchronizer{int newShareCount=0;
        Syn(int shareCount){if (shareCount <= 0) {throw new IllegalArgumentException("share count must large than zero");
            }// 設(shè)置初始共享同步狀態(tài)            setState(shareCount);
        }/** * 共享鎖指定數(shù)目
         * @param reduceShareCount
         * @return */@Overrideprotected int tryAcquireShared(int reduceShareCount) {for (;;){int currentShareCount = getState();
                newShareCount = currentShareCount- reduceShareCount;if (newShareCount < 0 ||compareAndSetState(currentShareCount,newShareCount)) {// newShareCount大于等于0才說明獲取鎖成功if (newShareCount >= 0) {//                        System.out.println(Thread.currentThread().getName()+" hold lock, current share count is "+newShareCount+", "+new Date());                    }// newShareCount小于0表示獲取失敗所以需要返回// compareAndSetState(currentShareCount,newShareCount)為true自然表示成功需要返回return newShareCount;
                }
            }
        }

        @Overrideprotected boolean tryReleaseShared(int returnShareCount) {for (;;){int currentShareCount = getState();
                newShareCount = currentShareCount + returnShareCount;if (compareAndSetState(currentShareCount,newShareCount)) {//                    System.out.println(Thread.currentThread().getName() +" release lock, current share count is "+newShareCount+", "+new Date());return true;
                }
            }
        }protected int getShareCount(){return getState();
        }
    }/** * 調(diào)用內(nèi)部同步器Syn的acquireShare方法     */@Overridepublic void lock() {
        syn.acquireShared(1);
    }/** * 調(diào)用內(nèi)部同步器Syn的releaseShared方法     */@Overridepublic void unlock() {
        syn.releaseShared(1);
    }

    @Overridepublic void lockInterruptibly() throws InterruptedException {if (Thread.interrupted()) {throw new IllegalStateException();
        }
        syn.acquireInterruptibly(1);
    }

    @Overridepublic boolean tryLock() {return false;
    }

    @Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;
    }


    @Overridepublic Condition newCondition() {return null;
    }
}

(2)測試Demo

public class ShareLockTest {

    @Testpublic void test(){final MyShareLock lock = new MyShareLock();class Worker extends Thread {
            @Overridepublic void run() {// 一直不停在獲取鎖while (true) {
                lock.lock();try {
                    System.out.println(Thread.currentThread().getName() +" hold lock, "+new Date());//                    System.out.println(lock.getSharedQueuedThreads());Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() +" release lock, "+new Date());
                }
            }
            }

        }for (int i = 0; i < 10; i++) {
            Worker worker = new Worker();// 以守護進程運行,VM退出不影響運行,這里只是為了一個打印效果,去掉注釋一直打印worker.setDaemon(true);
            worker.start();
        }// 每隔一秒換行for (int j = 0; j < 10; j++) {try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println();
        }
    }
}

(3)運行結(jié)果(結(jié)果可能不同)

Thread-1 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 release lock, Tue Dec 08 16:36:06 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-1 release lock, Tue Dec 08 16:36:06 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-4 release lock, Tue Dec 08 16:36:07 CST 2020Thread-2 release lock, Tue Dec 08 16:36:07 CST 2020Thread-5 hold lock, Tue Dec 08 16:36:07 CST 2020Thread-8 hold lock, Tue Dec 08 16:36:07 CST 2020Thread-8 release lock, Tue Dec 08 16:36:08 CST 2020Thread-3 hold lock, Tue Dec 08 16:36:08 CST 2020Thread-9 hold lock, Tue Dec 08 16:36:08 CST 2020Thread-5 release lock, Tue Dec 08 16:36:08 CST 2020Thread-6 hold lock, Tue Dec 08 16:36:09 CST 2020Thread-7 hold lock, Tue Dec 08 16:36:09 CST 2020Thread-3 release lock, Tue Dec 08 16:36:09 CST 2020Thread-9 release lock, Tue Dec 08 16:36:09 CST 2020Thread-6 release lock, Tue Dec 08 16:36:10 CST 2020Thread-1 hold lock, Tue Dec 08 16:36:10 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:10 CST 2020Thread-7 release lock, Tue Dec 08 16:36:10 CST 2020Thread-1 release lock, Tue Dec 08 16:36:11 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:11 CST 2020Thread-0 release lock, Tue Dec 08 16:36:11 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:11 CST 2020Thread-2 release lock, Tue Dec 08 16:36:12 CST 2020Thread-8 hold lock, Tue Dec 08 16:36:12 CST 2020Thread-5 hold lock, Tue Dec 08 16:36:12 CST 2020Thread-4 release lock, Tue Dec 08 16:36:12 CST 2020Thread-5 release lock, Tue Dec 08 16:36:13 CST 2020Thread-9 hold lock, Tue Dec 08 16:36:13 CST 2020Thread-3 hold lock, Tue Dec 08 16:36:13 CST 2020Thread-8 release lock, Tue Dec 08 16:36:13 CST 2020Thread-3 release lock, Tue Dec 08 16:36:14 CST 2020Thread-7 hold lock, Tue Dec 08 16:36:14 CST 2020Thread-9 release lock, Tue Dec 08 16:36:14 CST 2020Thread-6 hold lock, Tue Dec 08 16:36:14 CST 2020

(4)結(jié)果分析

該指定共享線程數(shù)量N的共享鎖的最終目的就是多個線程可以持有鎖(同步狀態(tài)),達到共享線程數(shù)量N(代碼中默認為2)時,其它線程將進入Queue等待獲取同步結(jié)果,同一時刻只能最多有N個線程持有鎖。

同樣地,我們分析開頭運行結(jié)果:

Thread-1 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 release lock, Tue Dec 08 16:36:06 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-1 release lock, Tue Dec 08 16:36:06 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:06 CST 2020

10個線程不停競爭鎖,一開始Thread-0與Thread-1在16:36:05時刻同時獲取到了鎖,此時已經(jīng)達到共享數(shù)量的最大值,即N,之后持有鎖1秒,Thread-0與Thread-1在16:36:06時刻立馬釋放鎖,同時Thread-4與Thread-2立馬退出等待隊列立馬競爭持有鎖。

從結(jié)果來看,完全是符合ShareLock共享鎖功能的:同一時刻最多允許N個線程持有鎖,其它線程等待持有線程釋放鎖!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多