是用於控制某個資源同一時間被線程訪問的個數,提供acquire()和release()方法,acquire獲取一個許可,若是沒有獲取的到就等待,release是在操做完成後釋放一個許可,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個執行直到把線程池中的線程執行完。在打印的日誌中咱們也能夠看到是每三個每三個打印。數據庫
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