• 并发工具类简介


    并发工具类

    1. CountDownLatch:闭锁,也叫线程递减锁。对线程进行计数,在计数归零之前线程会陷入阻塞;直到计数归零为止,才会放开阻塞。

      用给定的计数初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

      • 内部采用共享锁来实现
      import java.util.concurrent.CountDownLatch;
      /**
       * 模拟:考试
       * 需求:考官(线程A1,A2)和考生(线程B1、B2、B3、B4)
       *      都到考场后才能开始考试
       */
      public class CountDownLatchDemo {
          public static void main(String[] args) throws InterruptedException {
              CountDownLatch cdl = new CountDownLatch(6);
              new Thread(new Teacher(cdl)).start();
              new Thread(new Teacher(cdl)).start();
              new Thread(new Student(cdl) ).start();
              new Thread(new Student(cdl)).start();
              new Thread(new Student(cdl)).start();
              new Thread(new Student(cdl)).start();
      
              //计数减为1时自然唤醒
              cdl.await();
              System.out.println("开始考试");
          }
      }
      
      class Teacher implements Runnable{
          private CountDownLatch cdl;
          public Teacher(CountDownLatch cdl) {
              this.cdl = cdl;
          }
      
          @Override
          public void run() {
              try{
                  //放慢速度
                  Thread.sleep((long)(Math.random()*10000));
                  cdl.countDown();
                  System.out.println("考官到了");
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      
      class Student implements Runnable{
          private CountDownLatch cdl;
          public Student(CountDownLatch cdl) {
              this.cdl = cdl;
          }
      
          @Override
          public void run() {
              try{
                  Thread.sleep((long)(Math.random()*10000));
                  cdl.countDown();
                  System.out.println("考生到了");
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      
    2. CyclicBarrier:栅栏。对线程进行计数,在计数归零之前线程会陷入阻塞;直到计数归零为止,才会放开阻塞。一组线程到达同一个点后再分别继续执行。(与闭锁相比,并没有结束线程)

      它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。通俗讲:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。一组线程结束后开启另一组线程。

      • 底层采用ReentrantLock + Condition实现
      • 应用场景:多线程结果合并的操作,用于多线程计算数据,最后合并计算结果的应用场景
      import java.util.concurrent.CyclicBarrier;
      
      /**
       * 所有运动员跑到起跑线之后听到命令才能跑出去
       */
      public class CyclicBarrierDemo {
          public static void main(String[] args) {
              CyclicBarrier cb = new CyclicBarrier(5);
      
              new Thread(new Runner(cb),"1号").start();
              new Thread(new Runner(cb),"2号").start();
              new Thread(new Runner(cb),"3号").start();
              new Thread(new Runner(cb),"4号").start();
              new Thread(new Runner(cb),"5号").start();
      
          }
      }
      
      class Runner implements Runnable{
          private CyclicBarrier cb;
          public Runner(CyclicBarrier cb) {
              this.cb = cb;
          }
      
          @Override
          public void run() {
              try{
                  //模拟运动员走到起跑线的时间
                  Thread.sleep((long)(Math.random()*10000));
                  String name = Thread.currentThread().getName();
                  System.out.println(name+"运动员到了起跑线");
                  /**
                   * 先到的运动员应该阻塞,
                   * 直到所有的运动员都到了起跑线才能往外跑。
                   * 当计数归零的时候,自然苏醒
                   */
                  cb.await();
                  System.out.println(name+"运动员跑了出去");
      
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      
    3. Exchanger:交换机。用于交换两个线程之间的信息。

      具体来说,Exchanger类允许在两个线程之间定义同步点。当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程中,第二个线程的数据结构进入到第一个线程中

      import java.util.concurrent.Exchanger;
      
      public class ExchangerDemo {
          public static void main(String[] args) {
              Exchanger<String> stringExchanger = new Exchanger<>();
              new Thread(new Producer(stringExchanger)).start();
              new Thread(new Consumer(stringExchanger)).start();
          }
      }
      
      class Producer implements  Runnable{
          private Exchanger<String> ex;
          public Producer(Exchanger<String> ex) {
              this.ex = ex;
          }
      
          @Override
          public void run() {
              String info="商品";
              try {
                  String msg = ex.exchange(info);
                  System.out.println("生产者收到消费者的:"+msg);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      
      class Consumer implements Runnable{
          private Exchanger<String> ex;
          public Consumer(Exchanger<String> ex) {
              this.ex = ex;
          }
      
          @Override
          public void run() {
              String info="钱";
              try {
                  // 消费者将钱给生产者,应该收到生产者换过来的商品
                  String exchange = ex.exchange(info);
                  System.out.println("消费者收到生产者的:"+exchange);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      
    4. Semaphore:信号量。线程在获取信号之后执行代码,而在信号被全部占用之后,后来的线程需要阻塞,直到前面的线程释放信号,阻塞的线程才能获取信号执行逻辑。实际开发中,常用于限流。

      【作用】限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。

      从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。

      信号量Semaphore是一个非负整数(>=1)。当一个线程想要访问某个共享资源时,它必须要先获取Semaphore,当Semaphore >0时,获取该资源并使Semaphore – 1。如果Semaphore值 = 0,则表示全部的共享资源已经被其他线程全部占用,线程必须要等待其他线程释放资源。当线程释放资源时,Semaphore则+1

      • 内部采用共享锁实现
      • 应用场景:通常用于限制可以访问某些资源(物理或逻辑的)的线程数目
      import java.util.concurrent.Semaphore;
      
      public class SemaphoreDemo {
          public static void main(String[] args) {
              Semaphore s = new Semaphore(5);
              for(int i=0;i<7;i++){
                  new Thread(new Easter(s)).start();
              }
      
          }
      }
      
      //用餐的人
      class Easter implements Runnable{
          private Semaphore s;
          public Easter(Semaphore s) {
              this.s = s;
          }
      
          /**
           * 桌子的数量是有限的
           * 如果桌子被全部占用,后来的客人就需要等待
           * 桌子相当于信号,只要有信号,就可以使用
           */
          @Override
          public void run() {
              try{
                  s.acquire();
                  System.out.println("来了一波客人,占用了一张桌子~~~");
                  //模拟吃饭的时间
                  Thread.sleep((long) (Math.random() * 10000));
                  System.out.println("客人买单离开,空出一张桌子~~~");
                  // 释放1个信号,被阻塞的线程就可以获取信号执行代码
                  s.release();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      

    CountDownLatch与CyclicBarrier区别

    1. CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
    2. CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
    3. CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务。
  • 相关阅读:
    @WebFilter注解
    Value '0000-00-00' can not be represented as java.sql.Date解决办法
    项目配置 xml文件时 报错提示(The reference to entity "useSSL" must end with the ';' delimiter.)
    eclipse中取消自动生成的TODO Auto-generated method stub
    eclipse中把选中的代码全部变成大写或者小写的快捷键
    在浏览器访问Tomcat的时候报错java.lang.IllegalArgumentException: Control character in cookie value or attribute.
    Tomcat报错,内存溢出的错误Exception in thread "http-bio-8080-exec-13" java.lang.OutOfMemoryError: PermGen space
    java.io.Serializable的作用
    idea快捷键总结
    MYSQL分页limit速度太慢优化方法
  • 原文地址:https://www.cnblogs.com/juzhuxiaozhu/p/13296218.html
Copyright © 2020-2023  润新知