本博客系列是學習併發編程過程當中的記錄總結。因爲文章比較多,寫的時間也比較散,因此我整理了個目錄貼(傳送門),方便查閱。html
併發編程系列博客傳送門java
Semaphore([' seməf :(r)])的主要做用是控制線程併發的數量。咱們能夠將Semaphore想象成景區的一個門衛,這個門衛負責發放景區入園的許可證。編程
景區爲了遊客的入園觀賞體驗,決定最多容許200個有個同時在園內觀賞。那麼這個門衛在天天開園的時候手中都會有200張許可證,每當一個遊客要入園的時候門衛會給遊客發放一張許可證,當門衛手中的許可證發完以後再有遊客須要入園的話就必須等待。api
當遊客觀賞完畢以後,出園的時候須要將許可證交還到門衛手上。門衛將這些交還的許可證再發等待的遊客,這些遊客就能順利入園了。數組
Semaphore的API使用起來也比較簡單,常見的API簡介以下:安全
下面給出一個Oracle官方文檔中的列子代碼:併發
class Pool { // 可同時訪問資源的最大線程數 private static final int MAX_AVAILABLE = 100; private final Semaphore available = new Semaphore(MAX_AVAILABLE, true); // 共享資源 protected Object[] items = new Object[MAX_AVAILABLE]; protected boolean[] used = new boolean[MAX_AVAILABLE]; public Object getItem() throws InterruptedException { available.acquire(); return getNextAvailableItem(); } public void putItem(Object x) { if (markAsUnused(x)) available.release(); } private synchronized Object getNextAvailableItem() { for (int i = 0; i < MAX_AVAILABLE; ++i) { if (!used[i]) { used[i] = true; return items[i]; } } return null; } private synchronized boolean markAsUnused(Object item) { for (int i = 0; i < MAX_AVAILABLE; ++i) { if (item == items[i]) { if (used[i]) { used[i] = false; return true; } else return false; } } return false; } }
items數組能夠當作是咱們的共享資源,當有線程嘗試使用共享資源時,咱們要求線程先得到「許可」(調用Semaphore 的acquire方法),這樣線程就擁有了權限,不然就須要等待。當使用完資源後,線程須要調用Semaphore 的release方法釋放許可。dom
有些書中提到:若是將Semaphore的許可證數量設置成1的話,就能實現synchronized的功能。其實這種說法是不對的。ide
下面使用Semaphore來控制對一個帳戶進行併發存錢和取錢的動做,若是Semaphore能實現synchronized的功能的話,帳戶最後的餘額應該仍是10000,但代碼執行後的結果並非這樣。你們能夠執行下面的代碼看下結果。學習
public static final int THREAD_COUNT = 100; public static void main(String[] args) { BankAccount myAccount = new BankAccount("accountOfMG", 10000.00); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(new Runnable() { @Override public void run() { try { int var = new Random().nextInt(100); Thread.sleep(var); } catch (InterruptedException e) { e.printStackTrace(); } double deposit = myAccount.deposit(1000.00); System.out.println(Thread.currentThread().getName() + " balance1:" + deposit); } }).start(); } for (int i = 0; i < THREAD_COUNT; i++) { new Thread(new Runnable() { @Override public void run() { try { int var = new Random().nextInt(100); Thread.sleep(var); } catch (InterruptedException e) { e.printStackTrace(); } double deposit = myAccount.withdraw(1000.00); System.out.println(Thread.currentThread().getName() + " balance2:" + deposit); } }).start(); } } private static class BankAccount { Semaphore semaphore = new Semaphore(1); String accountName; double balance; public BankAccount(String accountName, double balance) { this.accountName = accountName; this.balance = balance; } public double deposit(double amount) { try { semaphore.acquire(); balance = balance + amount; return balance; } catch (Exception e) { throw new RuntimeException("中斷..."); } finally { semaphore.release(); } } public double withdraw(double amount) { try { semaphore.acquire(); balance = balance - amount; return balance; } catch (Exception e) { throw new RuntimeException("中斷..."); } finally { semaphore.release(); } } }
這裏Semaphore並不能實現synchronized的功能的緣由是:Semaphore並不能保證共享變量的可見性。
Semaphore底層原理仍是基於AQS機制的。這邊就不具體分析了,感興趣的能夠參考我前面關於AQS的文章。