本博客系列是学习并发编程过程当中的记录总结。因为文章比较多,写的时间也比较散,因此我整理了个目录贴(传送门),方便查阅。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的文章。