並發之AQS原理(一) 原理介紹簡單使用


並發之AQS原理(一)

如果說每一個同步的工具各有各的強大,那么這個強大背后是一個相同的動力,它就是AQS.

AQS是什么

AQS是指java.util.concurrent.locks包里的AbstractQueuedSynchronizer類,抽象的FIFO隊列式的同步器,AQS定義了一套多線程訪問共享資源的同步器框架

在了解AQS之前先了解下

先入先出隊列(First Input First Output,FIFO)這是一種傳統的按序執行方法,先進入的指令先完成並引退,跟着才執行第二條指令。

就是通常情況下的排隊。

AQS簡單的來說就是使用了一個共享變量來同步狀態,該狀態由子類去維護,而AQS框架做的是

  • 線程阻塞隊列的維護。
  • 線程阻塞和喚醒。
    共享變量的操作通過Unsafe類提供的CAS操作完成的。AQS類主要使用的就是2個方法 acquirerelease

acquire(獲得鎖):返回true則放線程成繼續執行,否則將線程加入等待隊列中,等待喚醒.

release(釋放鎖):用於釋放鎖。釋放當前線程隊列中的頭結點,然后調用喚醒下一個結點的方法。

AQS的簡單使用

一般的來說AQS的使用方式是繼承,用的是模板方法模式。

下面我們來用一個簡單的AQS實現來逐步講解下AQS的原理。

EasyLock簡單實現AQS

首先實現一個簡單的鎖就叫EasyLock吧

/**
 * 簡單不可重入鎖實現
 *
 */
public class EasyLock extends AbstractQueuedSynchronizer {

    /**
     * 鎖定
     */
    public void lock(){
        acquire(1);
    }

    /**
     * 嘗試鎖定
     */
    public boolean tryLock(){
        return tryAcquire(1);
    }

    /**
     * 解鎖
     */
    public void unlock(){
        release(1);
    }

    /**
     * 是否為鎖定
     */
    public boolean isLocked(){
        return isHeldExclusively();
    }

    /**
     * 嘗試獲取鎖
     */
    @Override
    protected boolean tryAcquire(int arg) {
        if(compareAndSetState(0,1)){
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    /**
     * 嘗試釋放鎖
     */
    @Override
    protected boolean tryRelease(int arg) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
}

測試


public class AqsExampleDemo {
    private static final int clientTotal = 30000;
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        final EasyLock easyLock = new EasyLock();
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            executorService.execute(() -> {
                try {
                    easyLock.lock();
                    count++;
                    easyLock.unlock();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }

        countDownLatch.await();
        System.out.println("統計次數:" + count);

    }
}

可以看出打印的結果是5000,並沒有出現並發問題。

總結

可以看出AQS的使用非常簡單幾乎只需要重寫 tryAcquire、tryRelease就可以自己實現一個鎖。
下一篇我們講解下AQS中底層的CLH隊列是如何保證其並發性的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM