AQS 啓發--自己實現一個ReentrantLock的一部分(獨佔式非公平鎖)

ReentrantLock

什麼是ReentrantLock ?
ReentrantLock 中文翻譯過來就是可重入鎖,也就是同一個線程這個鎖是可以重複獲取的 ,synchronize關鍵字就是一個隱式的可重入鎖。
ReentrantLock 的實現原理
ReentrantLock 是實現了Lock接口使得它能夠作爲一個鎖被使用,同時他還有一個內部類同步器:Sync ,這個類繼承了AQS(AbstractQueuedSynchronizer) 類,然後實現AQS裏面的模板方法。
在這裏插入圖片描述

自己實現一個ReentrantLock

這裏我們來實現一個獨佔式的非公平鎖
代碼骨架搭建
要實現一個 ReentrantLock 首先的實現Lock接口,如下:


class MyReentrantLock implements Lock{
    /**
     * 加鎖方法
     */
    @Override
    public void lock() {

    }

    /**
     * 中斷加鎖方法
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    /**
     * 嘗試加鎖
     * @return
     */
    @Override
    public boolean tryLock() {
        return false;
    }

    /**
     * 在規定的時間內 嘗試加鎖
     * @param time
     * @param unit
     * @return
     * @throws InterruptedException
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    /**
     * 解鎖
     */
    @Override
    public void unlock() {

    }

    /**
     * 線程條件 裏面的方法與Object裏面的 wait,notify,notifyAll 等方法類似
     * @return
     */
    @Override
    public Condition newCondition() {
        return null;
    }
}

同時還需要有一個基於AQS的同步器
這裏我們在上面的類裏面寫一個內部類來繼承AQS 同時重寫下面 的三個方法
在這裏插入圖片描述
到這裏這個ReentrantLock 的骨架就已經搭建好了,下面就是實現部分
實現MySync同步器

   /**
     * 自定義的同步器,僅僅實現
     */
    class MySync extends AbstractQueuedSynchronizer {
        /**
         * 嘗試獲取鎖
         * @param acquire
         * @return
         */
        @Override
        protected boolean tryAcquire(int acquire) {
           // System.out.println("加鎖");
        //1、獲取當前的線程
            Thread thread = Thread.currentThread();
        //2、獲取當前的線程狀態
            int state = getState() ;
        //3、判斷狀態是不是爲0,如果爲0那麼就是說明當前鎖還未被持有,很有可能有其他線程來獲取該鎖,那麼需要進行CAS操作來設置鎖的持有狀態
            if(state==0 && compareAndSetState(0,acquire)){
                //獲取鎖成功,將持有線程設置爲當前線程
                setExclusiveOwnerThread(thread);
                return true;
                //由於是獨佔式鎖,那麼如果是當前線程持有的鎖那麼就只需要設置狀態+1就好了
                //否則就獲取不成功
            }else if(thread==getExclusiveOwnerThread()){
                if(state+acquire>=0){
                    setState(state+acquire);
                }
                return true;
            }
            return false;
        }
        /**
         * 嘗試釋放鎖
         * @param release
         * @return
         */
        @Override
        protected boolean tryRelease(int release) {
            //釋放鎖的時候有兩點需要注意
            // 1、釋放鎖的時候肯定是隻有持有鎖的線程才能來釋放鎖,所以不需要CAS操作
            //2、釋放鎖的時候只有當狀態爲0 的時候纔算釋放完成
            Thread thread = Thread.currentThread();
            if(thread!=getExclusiveOwnerThread()){
               throw new RuntimeException("非法操作");
            }
            int state = getState()-release;
            boolean flag = false;
            //如果狀態爲0
            if(state==0){
                //將持有線程置空
                setExclusiveOwnerThread(null);
                flag= true;
            }
            //設置線程狀態
            setState(state);
            //System.out.println("釋放");
            return flag;
        }
        /**
         * 是不是當前線程持有鎖
         * @return
         */
        @Override
        protected boolean isHeldExclusively() {
            return getExclusiveOwnerThread()==Thread.currentThread();
        }

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

lock 接口的實現

/**
     * 加鎖方法
     */
    @Override
    public void lock() {
        this.mySync.acquire(1);
    }
    /**
     * 中斷加鎖方法
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        this.mySync.acquireInterruptibly(1);
    }
    /**
     * 嘗試加鎖
     * @return
     */
    @Override
    public boolean tryLock() {
        return this.mySync.tryAcquire(1);
    }
    /**
     * 在規定的時間內 嘗試加鎖
     * @param time
     * @param unit
     * @return
     * @throws InterruptedException
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return this.mySync.tryAcquireNanos(1, unit.toNanos(time));
    }

    /**
     * 解鎖
     */
    @Override
    public void unlock() {
        this.mySync.release(1);
    }

    /**
     * 線程條件 裏面的方法與Object裏面的 wait,notify,notifyAll 等方法類似
     * @return
     */
    @Override
    public Condition newCondition() {
        return this.mySync.newCondition();
    }

到這裏自定義的 ReentrantLock(獨佔式非公平鎖) 就實現完成了

下面我們看一下效果

測試

我們寫如下一段測試代碼來測試是否成功
其中ThreadFactory 是自定義的一個線程工廠,可以不用傳入

public class SelfLock {

    public static void main(String[] args) {
        final MyReentrantLock reentrantLock  = new MyReentrantLock();
        final List<Integer> list = new ArrayList<>();
        final CountDownLatch countDownLatch = new CountDownLatch(100);
        final AtomicReference<MyInteger> atomicI = new AtomicReference<MyInteger>(new MyInteger(4999));
        for (int i = 0; i < 5000; i++) {
            list.add(i);
        }
        final MyInteger j = new MyInteger(4999);
        /*定義線程工廠 方便bug回溯追蹤*/
        ThreadFactory customThreadfactory = new ThreadFactoryBuilder()
                .setNamePrefix("測試-Thread").setDaemon(false)
                .setPriority(Thread.MAX_PRIORITY).build();
        /*keepAliveTile 線程最大空閒時間 */
        ExecutorService executorService =
                new ThreadPoolExecutor(100, 200, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10),customThreadfactory);
        long b = System.currentTimeMillis();
        for(int i=0;i<100 ;i++){
            executorService.execute(() -> {
                        while (true) {
                          //   int j = atomicInteger.getAndDecrement();
                            if (j.getIndex() >= 0) {
                                reentrantLock.lock();
                                System.out.println("線程"+Thread.currentThread().getName()+"====》" +list.get(j.getIndex()));
                                j.setIndex(j.getIndex()-1);
                                reentrantLock.unlock();
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                break;
                            }
                        }
                countDownLatch.countDown();
                    }
            );
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("耗時---->" + (System.currentTimeMillis() - b));
        executorService.shutdown();

    }

}

class MyInteger{
    int index;
    public MyInteger(int i){
        this.index=i;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
}

測試效果:

在這裏插入圖片描述
測試成功,沒有發生線程不安全的情況

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