AQS同步組件--Semaphore

Semaphore

什麼是Semaphore?
是用於控制某個資源同一時間被線程訪問的個數,提供acquire()和release()方法,acquire獲取一個許可,若是沒有獲取的到就等待,release是在操做完成後釋放一個許可,Semaphore維護了當前訪問的個數,經過同步機制來控制同時訪問的個數,在數據結構裏鏈表中的節點是能夠無限個的,而Semaphore裏維護的是一個有大小的限鏈表。
Semaphore的使用場景
Semaphore用於僅能提供有限訪問的資源,好比數據庫中的連接數只有20可是咱們上層應用數可能遠大於20,若是同時都對數據庫連接進行獲取,那很定會由於連接獲取不到而報錯,因此咱們就要對數據庫連接的訪問進行控制。
演示代碼
@Slf4j
public class SemaphoreExample1 {

    private final static int threadCount = 20;

    public static void main(String[] args) throws Exception {

        ExecutorService exec = Executors.newCachedThreadPool();

        final Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    semaphore.acquire(); // 獲取一個許可
                    test(threadNum);
                    semaphore.release(); // 釋放一個許可
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }

    private static void test(int threadNum) throws Exception {
        log.info("{}", threadNum);
        Thread.sleep(1000);
    }
}

咱們在執行 test(threadNum)方式先後包裹上acquire和release,這樣其實咱們就至關於一個單線程在執行。當執行acquire後就只能等待執行release後再執行新的線程,而後咱們在acquire()和release()都是沒有傳參也就是1,每次只容許一個線程執行,若是咱們改爲java

semaphore.acquire(3); // 獲取多個許可
test(threadNum);
semaphore.release(3); // 釋放多個許可

那麼咱們就是每3個3個執行直到把線程池中的線程執行完。在打印的日誌中咱們也能夠看到是每三個每三個打印。數據庫

假設咱們的數據庫容許獲取鏈接是3剩餘的獲取線程咱們不想要只想丟棄改如何實現?
Semaphore提供了一個嘗試獲取許可的方法,tryAcquire()嘗試獲取許可成功就執行,嘗試獲取許可失敗就丟棄線程。下面看代碼
@Slf4j
public class SemaphoreExample3 {

    private final static int threadCount = 20;

    public static void main(String[] args) throws Exception {

        ExecutorService exec = Executors.newCachedThreadPool();

        final Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    if (semaphore.tryAcquire()) { // 嘗試獲取一個許可
                        test(threadNum);
                        semaphore.release(); // 釋放一個許可
                    }
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }

    private static void test(int threadNum) throws Exception {
        log.info("{}", threadNum);
        Thread.sleep(1000);
    }
}

這段代碼執行結果就只打印了3行日誌,其餘的線程就被丟棄了。tryAcquire()共提供以下幾種方法。數據結構

咱們用一個例子來演示一下參數的方法的使用。ui

@Slf4j
public class SemaphoreExample4 {

    private final static int threadCount = 20;

    public static void main(String[] args) throws Exception {

        ExecutorService exec = Executors.newCachedThreadPool();

        final Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    if (semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)) { // 嘗試獲取一個許可
                        test(threadNum);
                        semaphore.release(); // 釋放一個許可
                    }
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }

    private static void test(int threadNum) throws Exception {
        log.info("{}", threadNum);
        Thread.sleep(1000);
    }
}

此次咱們使用的是一個tryAcquire(5000, TimeUnit.MILLISECONDS))方法,這個方法的第一個參數是表示等待5000毫秒,第二參數是表示多長時間嘗試一次,TimeUnit.MILLISECONDS表示1毫秒。這時候咱們會發現20個線程都執行了,爲何會這樣呢?由於咱們在執行時等待超時時間是5秒,每次執行就是sleep 1秒,因此能夠獲取成tryAcquire進而執行。spa

相關文章
相關標籤/搜索