并发工具类的使用 CountDownLatch,CyclicBarrier,Semaphore,Exchanger
1.CountDownLatch
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
A CountDownLatch
用给定的计数初始化。 await
方法阻塞,直到由于countDown()
方法的调用而导致当前计数达到零,之后所有等待线程被释放,并且任何后续的await
调用立即返回。 这是一个一次性的现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier
。
public class CountDownLatchTest { public void meeting (CountDownLatch countDownLatch) { System.out.println(Thread.currentThread().getName() + "到达会议室,等待开会。。。"); try { countDownLatch.countDown(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { //CountDownLatch 传入线程数,当传入的线程数全部执行完成后再往后执行,否则一直等待 CountDownLatch countDownLatch = new CountDownLatch(3); CountDownLatchTest countDownLatchTest = new CountDownLatchTest(); Runnable runnable = () -> countDownLatchTest.meeting(countDownLatch); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("全部线程执行完毕"); } }
2.CyclicBarrier
允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。
A CyclicBarrier
支持一个可选的Runnable
命令,每个屏障点运行一次,在派对中的最后一个线程到达之后,但在任何线程释放之前。 在任何一方继续进行之前,此屏障操作对更新共享状态很有用。
public class CyclicBarrierTest { public void meeting (CyclicBarrier cyclicBarrier) { System.out.println(Thread.currentThread().getName() + "到达会议室,等待开会。。。"); try { cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + "发言中。。。"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { //CyclicBarrier构造方法一,传入线程数,当传入的线程数全部执行完成后再往后执行,否则一直等待 CyclicBarrier cyclicBarrier = new CyclicBarrier(3); //CyclicBarrier构造方法二,传入线程数,当传入的线程数全部执行完成后再往后执行,否则一直等待;传入最后一个线程执行完成后将执行的代码 从写runnable接口 CyclicBarrier cyclicBarrier1 = new CyclicBarrier(3, () -> System.out.println(Thread.currentThread().getName() + "已经全部到达,开始开会")); CyclicBarrierTest cyclicBarrierTest = new CyclicBarrierTest(); Runnable runnable = () -> cyclicBarrierTest.meeting(cyclicBarrier1); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start(); } }
3.Semaphore
一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()
都会阻塞,直到许可证可用,然后才能使用它。 每个release()
添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore
只保留可用数量的计数,并相应地执行
信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。
public class SemaphoreTest { public void method(Semaphore semaphore) { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+" is run ..."); Thread.sleep(2000); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { SemaphoreTest semaphoreTest = new SemaphoreTest(); Semaphore semaphore = new Semaphore(10); while (true) { new Thread(() -> semaphoreTest.method(semaphore)).start(); } } }
4.Exchanger
线程可以在成对内配对和交换元素的同步点。 每个线程在输入exchange
方法时提供一些对象,与合作者线程匹配,并在返回时接收其合作伙伴的对象。 交换器可以被视为一个的双向形式SynchronousQueue
。 交换器在诸如遗传算法和管道设计的应用中可能是有用的。
public class ExchangerTest { public void a(Exchanger<String> exchanger) { System.out.println("a 方法开始执行。。。"); try { System.out.println("a 方法开始抓取数据..."); Thread.sleep(4000); System.out.println("a 方法抓取数据结束"); String res = "12345"; System.out.println("a 等待对比结果"); exchanger.exchange(res); } catch (InterruptedException e) { e.printStackTrace(); } } public void b(Exchanger<String> exchanger) { System.out.println("b 方法开始执行。。。"); try { System.out.println("b 方法开始抓取数据。。。"); Thread.sleep(4000); System.out.println("b 方法抓取数据结束"); String res = "12345"; String value = exchanger.exchange(res); System.out.println("比对结果为:" + value.equals(res)); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ExchangerTest exchangerTest = new ExchangerTest(); Exchanger<String> exchanger = new Exchanger<>(); new Thread(() -> exchangerTest.a(exchanger)).start(); new Thread(() -> exchangerTest.b(exchanger)).start(); } }