控制并发流程的方式

CountDownLatch

  • 即倒数栅栏,有两种典型用法:一等多、多等一

  • await():调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行

  • countDown():将count()值减1,直到为0时,等待的线程会被唤醒

  • CountDownLatch(int count):构造函数,参数count为需要倒数的数值

  • 一等多:一个线程等待多个线程都执行完毕,再继续自己的工作

    模拟工厂中,质检流程,5个质检工人检查,所有人都认为通过,才算通过

      public class CountDownLatchDemo1 {
          public static void main(String[] args) throws InterruptedException {
              /**
               * 需要倒数5次
               */
              CountDownLatch latch = new CountDownLatch(5);
              ExecutorService service = Executors.newFixedThreadPool(5);
              for (int i = 0; i < 5; i++) {
                  final int no = i + 1;
      
                  Runnable runnable = new Runnable() {
                      @Override
                      public void run() {
                          try {
                              Thread.sleep((long) (Math.random()*10000));
                              System.out.println("No." + no + "完成了检查");
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          } finally {
                              /**
                               * 每个线程检查完都要countDown一次
                               */
                              latch.countDown();
                          }
                      }
                  };
                  service.submit(runnable);
              }
             
              System.out.println("等待5个人检查完....");
              /**
              * 每个线程都要在此等待,最后一个线程除外
              */
              latch.await();
              System.out.println("所有人都完成了工作,进入下一个环节");
          }
      }
    
  • 多等一:多个线程等待某一个线程的信号同时开始执行

    模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。

    public class CountDownLatchDemo2 {
         public static void main(String[] args) throws InterruptedException {
             CountDownLatch begin = new CountDownLatch(1);
             ExecutorService service = Executors.newFixedThreadPool(5);
             for (int i = 0; i < 5; i++) {
                 final int no = i + 1;
                 Runnable runnable = new Runnable() {
                     @Override
                     public void run() {
                         System.out.println("No." + no + "准备完毕,等待发令枪");
                         try {
                             begin.await();
                             System.out.println("No." + no + "开始跑步了");
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                     }
                 };
                 service.submit(runnable);
             }
     
             //检查发令枪
             Thread.sleep(5000);
             System.out.println("发令枪响,比赛开始!");
             begin.countDown();
         }
     }
    

Semaphore

  • Semaphore可以用来限制或管理数量有限的资源的使用情况

  • 使用流程

    1. 初始化Semaphore并指定许可证
    2. 在需要许可证的地方使用acquire()方法或者acquireUninterruptibly()方法获取
    3. 在任务结束后,调用release()来释放许可证
  • new Semaphore(int permits, boolean fair):构造函数,permits表示许可证数量,fair表示是否使用公平策略,如果传入true,则将等待获取许可证的线程放进FIFO队列中,有了新的许可证便分发给等待时间最长的线程

  • acquire():获取许可证

  • release():归还许可证

    /**信号量的基本用法**/
    public class SemaphoreDemo {
        static Semaphore semaphore = new Semaphore(3, true);
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(50);
            for (int i = 0; i < 100; i++) {
                service.submit(new Task());
            }
            service.shutdown();
        }
    
        static class Task implements Runnable{
    
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "拿到了许可证");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "释放了许可证");
                semaphore.release();
            }
        }
    }
    
  • 获取和释放的许可证数量必须一致,否则随着时间推移,最后许可证数量不够用,程序卡死

  • 获取和释放许可证对线程并无要求,这个线程获取,可由其它线程释放

Condition

  • 当线程1需要等待某个条件时,执行condition.await()方法,线程进入阻塞状态

  • 假设线程2去执行对应的条件,直到条件达成,线程2去执行condition.signal()方法,唤醒线程1

  • signal():公平,只会唤醒等待时间最长的那个线程

  • signalAll():唤醒所有的正在等待的线程

    /**
    * 基本用法
    */
    public class ConditionDemo1 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        void method1() throws InterruptedException {
            lock.lock();
            try {
                System.out.println("条件不满足,开始await");
                /**
                 * 条件不满足,开始阻塞
                 */
                condition.await();
                System.out.println("条件满足了,开始执行后续的任务");
            } finally {
                lock.unlock();
            }
        }
    
        void method2() {
            lock.lock();
            try {
                System.out.println("准备工作完成,唤醒其它线程");
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ConditionDemo1 conditionDemo1 = new ConditionDemo1();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                        conditionDemo1.method2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            conditionDemo1.method1();
        }
    }
    
  • 用条件对象实现生产者模式

    public class ConditionDemo2 {
        private int queueSize = 10;
        private PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize);
        private Lock lock = new ReentrantLock();
        private Condition notFull = lock.newCondition();
        private Condition notEmpty = lock.newCondition();
    
        public static void main(String[] args) {
            ConditionDemo2 conditionDemo2 = new ConditionDemo2();
            Producer producer = conditionDemo2.new Producer();
            Consumer consumer = conditionDemo2.new Consumer();
            producer.start();
            consumer.start();
        }
    
        class Consumer extends Thread{
            @Override
            public void run() {
                consume();
            }
    
            private void consume() {
                while (true) {
                    lock.lock();
                    try {
    
                        while (queue.size() == 0) {
                            System.out.println("队列空,等待数据");
                            try {
                                notEmpty.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
    
                        queue.poll();
                        notFull.signal();
                        System.out.println("从队列中取走了一个数据,队列剩余" + queue.size() + "个元素");
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    
        class Producer extends Thread{
            @Override
            public void run() {
                produce();
            }
    
            private void produce() {
                while (true) {
                    lock.lock();
                    try {
    
                        while (queue.size() == queueSize) {
                            System.out.println("队列满,等待有空余");
                            try {
                                notFull.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
    
                        queue.offer(1);
                        notEmpty.signal();
                        System.out.println("向队列中插入了一个数据,队列剩余空间" + (queueSize - queue.size()));
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    }
    

CyclicBarriar

  • CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞一组线程

    下面例子中演示有10个线程,5个一批5个一批地放行

    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
                @Override
                public void run() {
                    System.out.println("所有人都到场了,大家统一出发!");
                }
            });
            for (int i = 0; i < 10; i++) {
                new Thread(new Task(i, cyclicBarrier)).start();
            }
        }
    
        static class Task implements Runnable{
    
            private int id;
            private CyclicBarrier cyclicBarrier;
    
            public Task(int id, CyclicBarrier cyclicBarrier) {
                this.id = id;
                this.cyclicBarrier = cyclicBarrier;
            }
    
            @Override
            public void run() {
                System.out.println("线程" + id + "现在前往集合地点");
                try {
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("线程" + id + "到了集合地点,开始等待其他人到达");
                    cyclicBarrier.await();
                    System.out.println("线程" + id + "出发了");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  • CyclicBarrier和CountDownLatch的区别

    1. 作用不同:CyclicBarrier要等待固定数量的线程都到达了栅栏位置才能继续执行,而CountDownLatch只需等待数字到达0,也就是说,CountDownLatch用于事件,CyclicBarrier用于线程
    2. 可重用性不同:CountDownLatch在倒数到0并触发门闩打开后,就不能再次使用了,除非新建实例;而CyclicBarrier可以重复使用
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页