是用于控制某个资源同一时间被线程访问的个数,提供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