• Java并发编程之——同步器工具类(semaphores, barriers, CountDownLatch,exchangers)


    Semaphore:

    类java.util.concurrent.Semaphore提供了一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。调用acquire()时无法保持同步锁,因为这会阻止将数据项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态。按此方式使用,二进制信号量具有某种属性(与很多Lock实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。

    Semaphore可以实现信号灯,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数

    Semaphore sp = new Semaphored(个数);

    sp.availablePermits()当前可获得个数

    sp.acquire();取得信号灯

    sp.release();释放信号灯

     1 import java.util.concurrent.ExecutorService;
     2 import java.util.concurrent.Executors;
     3 import java.util.concurrent.Semaphore;
     4 
     5 public class SemaphoreTest {
     6 
     7     /**
     8      * @param args
     9      */
    10     public static void main(String[] args) {
    11         ExecutorService service = Executors.newCachedThreadPool();
    12         //最多3个许可,也就最多有3个线程同时访问
    13         final Semaphore sp = new Semaphore(3);
    14         for(int i=0;i<10;i++){
    15             Runnable runable = new Runnable() {
    16                 @Override
    17                 public void run() {
    18                     try{
    19                         //取得sq许可
    20                         sp.acquire();
    21                     }catch (Exception e) {
    22                         e.printStackTrace();
    23                     }
    24                     System.out.println(Thread.currentThread().getName()+"进入");
    25                     System.out.println("当前还有"+(3-sp.availablePermits())+"并发");
    26                     //释放许可
    27                     sp.release();
    28                 }
    29             };
    30             service.execute(runable);
    31             
    32         }
    33         service.shutdown();
    34     }
    35 
    36 }

    Barrier

    在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等所有线程都到达某一个阶段后再统一执行。这是CyclicBarrier很有用。它允许一组线程互相等待,直到到达某个公共屏障点。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier.它重要的属性就是参与者个数,另外最重要的方法是await(),当所有线程都调用了await()后,就表示这些线程可以继续执行,否则就会等待。CyclicBarrier支持一个可选 的Runnable命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作有用。

    CyclicBarrier:表示大家彼此等待,大家集合好后才开始出发,就是一定数组的线程在一些集合点汇合,只有所有线程都执行到后才开始下一步。

    CyclicBarrier cb = new CyclicBarrier(个数3);

    cb.await();

    cb.await();               cb.getNumberWaiting();

    cb.await();

     1 public class CyclicBarrierTest {
     2 
     3     public static void main(String[] args) throws IOException, InterruptedException {
     4         CyclicBarrier barrier = new CyclicBarrier(3);
     5 
     6         ExecutorService executor = Executors.newFixedThreadPool(3);
     7         executor.submit(new Thread(new Runner(barrier, "1号选手")));
     8         executor.submit(new Thread(new Runner(barrier, "2号选手")));
     9         executor.submit(new Thread(new Runner(barrier, "3号选手")));
    10 
    11         executor.shutdown();
    12     }
    13 }
    14 
    15 class Runner implements Runnable {
    16     // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
    17     private CyclicBarrier barrier;
    18 
    19     private String name;
    20 
    21     public Runner(CyclicBarrier barrier, String name) {
    22         super();
    23         this.barrier = barrier;
    24         this.name = name;
    25     }
    26 
    27     @Override
    28     public void run() {
    29         try {
    30             Thread.sleep(1000 * (new Random()).nextInt(8));
    31             System.out.println(name + " 准备好了...");
    32             // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
    33             barrier.await();
    34         } catch (InterruptedException e) {
    35             e.printStackTrace();
    36         } catch (BrokenBarrierException e) {
    37             e.printStackTrace();
    38         }
    39         System.out.println(name + " 起跑!");
    40     }
    41 }

    CountDownLatch

    在完成一组正在其他线程 中执行的操作之前,它允许一个或多个线程一直等待。用给定的数字作为计数器初始化CountDownLatch,一个线程调用await()方法后,在当前计数到达零之前,会一直受阻塞。其它线程调用countDown()方法,会使计数器减一,所以,计数器的值 为0后,会释放所有等待的线程。其它后续的await()调用都将立即返回。这种现象只出现一次,因为计数无法被重置。如果需要重置计数,请考虑使用CyclicBarrier.

    CountDownLatch作为一个通用同步工具,有很多用途。使用1初始化的CountDownLatch用作一个简单的开、关锁存器,或入口:在通过调用countDown()的线程打开入口前,所有调用await()的线程都一直在入口处等待。用N初始化的CountDownLatch可以使一个线程在N个线程完成某项操作之前一直等待,或者使其在某项操作完成N次之前一直等待。

    CountDownLatch:犹如倒计时器计数器,调用CountDownLatch对象的countDown

    方法就将计算器减1,当计数到达0时,则所有等待者或单个等待者开始执行。

    CountDownLatch cdOrder = new CountDownLatch(3);

    Exchanger

    java.util.concurrent.Exchanger提供了一个同步点,在这个同步点,一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收他的伙伴线程提供的数据,并返回。

    Exchager用于实现两个人之间的数据交换,每个人在完成一定的事务后与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿到数据到来时,才能彼此交换数据

  • 相关阅读:
    Intellij IDEA 配置Tomcat远程调试
    maven学习二(dependencies)
    maven学习一(HelloWorld工程)
    一致性hash在分布式系统中的应用
    理解TCP之Keepalive
    理解HTTP之keep-alive
    TCP/IP,http,socket,长连接,短连接
    图解 HTTP 协议
    PHP开发的一些趣事
    vue
  • 原文地址:https://www.cnblogs.com/wn398/p/3130979.html
Copyright © 2020-2023  润新知