• java.util.concurrent中的常用组件


    一. CountDownLatch

            一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

    CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

    CountDownLatch的主要方法:

                                方法名称                                       方法作用
    public CountDownLatch(int count) 构造器参数指定了计数的次数,即有几个独立的任务
    public void CountDown() 当前线程调用此方法,计数减一,即其中一个任务完成
    public void await() 当前线程调用此方法会一直被阻塞,直到计数为0,即所有子任务都完成了。
       1: package com.wbl.test.thread.countDownLatch;
       2:  
       3: import java.text.SimpleDateFormat;
       4: import java.util.Date;
       5: import java.util.concurrent.CountDownLatch;
       6:  
       7: /**
       8:  * Created with Simple_love
       9:  * Date: 2016/3/19.
      10:  * Time: 16:06
      11:  */
      12: public class CountDownLatchDemo {

    13: final static SimpleDateFormat

    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

      14:  
      15:         public static void main(String[] args) throws InterruptedException {
      16:                 CountDownLatch countDownLatch = new CountDownLatch(2);    //两个工人协同工作
      17:                 Worker worker1 = new Worker("work1",5000,countDownLatch);
      18:                 Worker worker2 = new Worker("work2",8000,countDownLatch);
      19:                 worker1.start();
      20:                 worker2.start();
      21:                 countDownLatch.await();   //等待所有的工人结束
      22:                 System.out.println("all work done at " + sdf.format(new Date()));
      23:         }
      24:  
      25:         static class Worker extends Thread{
      26:                 String name;
      27:                 int workTime;
      28:                 CountDownLatch countDownLatch;
      29:                 public Worker(String name,int workTime,CountDownLatch countDownLatch){
      30:                         this.name = name;
      31:                         this.workTime = workTime;
      32:                         this.countDownLatch = countDownLatch;
      33:                 }
      34:  
      35:                 public void doWork(){
      36:                         try {
      37:                                 Thread.sleep(workTime);
      38:                         }catch (InterruptedException e){
      39:                                 e.printStackTrace();
      40:                         }
      41:                 }
      42:  
      43:                 @Override
      44:                 public void run() {
      45:                         System.out.println("Worker " + name + " do work begin at " + sdf.format(new Date()));
      46:                         doWork();//工作了
      47:                        System.out.println("Worker "+name+" do work complete at "+sdf.format(new Date()));
      48:                        countDownLatch.countDown();//工人完成工作,计数器减一
      49:  
      50:                 }
      51:         }
      52: }

    CountDownLatch被设计为只触发一次,计数值不能被重置。一旦计数器的值初始后,唯一可以修改它的方法就是之前用的 countDown() 方法。当计数器到达0时, 全部调用 await() 方法会立刻返回,接下来任何countDown() 方法的调用都将不会造成任何影响。

    二. CyclicBarrier

    字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

    CyclicBarrier的主要方法

                                方法名称                                       方法作用
    public CyclicBarrier(int): 构造器参数指定线程的个数,即有几个互相等待的任务
    public void CyclicBarrier(int,Runnable):   当一组的线程全都到达某个状态后,开始执行Runnable对象
    public void await() 某个线程到达barrier状态

    CyclicBarrier的应用场景:在某种需求中,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择CyclicBarrier了。

    实例分析:我们需要统计全国的业务数据。其中各省的数据库是独立的,也就是说按省分库。并且统计的数据量很大,统计过程也比较慢。为了提高性能,快速计算。我们采取并发的方式,多个线程同时计算各省数据,最后再汇总统计。在这里CyclicBarrier就非常有用。

       1: /**  
       2: * 各省数据独立,分库存偖。为了提高计算性能,统计时采用每个省开一个线程先计算单省结果,最后汇总。  
       3: *   
       4: * @author guangbo email:weigbo@163.com  
       5: *   
       6: */  
       7: public class Total {   
       8:   
       9:    // private ConcurrentHashMap result = new ConcurrentHashMap();   
      10:   
      11:    public static void main(String[] args) {   
      12:        TotalService totalService = new TotalServiceImpl();   
      13:         CyclicBarrier barrier = new CyclicBarrier(5,   
      14:                 new TotalTask(totalService));   
      15:   
      16:         // 实际系统是查出所有省编码code的列表,然后循环,每个code生成一个线程。   
      17:         new BillTask(new BillServiceImpl(), barrier, "北京").start();   
      18:         new BillTask(new BillServiceImpl(), barrier, "上海").start();   
      19:         new BillTask(new BillServiceImpl(), barrier, "广西").start();   
      20:         new BillTask(new BillServiceImpl(), barrier, "四川").start();   
      21:         new BillTask(new BillServiceImpl(), barrier, "黑龙江").start();    
      22:    }   
      23: }   
      24:   
      25: /**  
      26:  * 主任务:汇总任务  
      27:  */  
      28: class TotalTask implements Runnable {   
      29:     private TotalService totalService;   
      30:   
      31:     TotalTask(TotalService totalService) {   
      32:         this.totalService = totalService;   
      33:     }   
      34:   
      35:     public void run() {   
      36:         // 读取内存中各省的数据汇总,过程略。   
      37:         totalService.count();   
      38:         System.out.println("=======================================");   
      39:         System.out.println("开始全国汇总");   
      40:     }   
      41: }   
      42:   
      43: /**  
      44:  * 子任务:计费任务  
      45:  */  
      46: class BillTask extends Thread {   
      47:     // 计费服务   
      48:     private BillService billService;   
      49:     private CyclicBarrier barrier;   
      50:     // 代码,按省代码分类,各省数据库独立。   
      51:     private String code;   
      52:   
      53:     BillTask(BillService billService, CyclicBarrier barrier, String code) {   
      54:         this.billService = billService;   
      55:        this.barrier = barrier;   
      56:         this.code = code;   
      57:     }   
      58:   
      59:     public void run() {   
      60:         System.out.println("开始计算--" + code + "省--数据!");   
      61:         billService.bill(code);   
      62:         // 把bill方法结果存入内存,如ConcurrentHashMap,vector等,代码略   
      63:         System.out.println(code + "省已经计算完成,并通知汇总Service!");   
      64:         try {   
      65:             // 通知barrier已经完成   
      66:             barrier.await();   
      67:         } catch (InterruptedException e) {   
      68:             e.printStackTrace();   
      69:         } catch (BrokenBarrierException e) {   
      70:             e.printStackTrace();   
      71:         }   
      72:     }   
      73:   
      74: }  

     

    CountDownLatch和CyclicBarrier的区别

    CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

    三. DelayQueue

    DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

    Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象。此接口的实现必须定义一个 compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序。

    (1) 实例场景

    模拟一个考试的日子,考试时间为120分钟,30分钟后才可交卷,当时间到了,或学生都交完卷了考试结束。

    这个场景中几个点需要注意:

    1. 考试时间为120分钟,30分钟后才可交卷,初始化考生完成试卷时间最小应为30分钟
    2. 对于能够在120分钟内交卷的考生,如何实现这些考生交卷
    3. 对于120分钟内没有完成考试的考生,在120分钟考试时间到后需要让他们强制交卷
    4. 在所有的考生都交完卷后,需要将控制线程关闭

    实现思想:用DelayQueue存储考生(Student类),每一个考生都有自己的名字和完成试卷的时间,Teacher线程对DelayQueue进行监控,收取完成试卷小于120分钟的学生的试卷。当考试时间120分钟到时,先关闭Teacher线程,然后强制DelayQueue中还存在的考生交卷。每一个考生交卷都会进行一次countDownLatch.countDown(),当countDownLatch.await()不再阻塞说明所有考生都交完卷了,而后结束考试。

       1: package com.wbl.test.thread.delayQueue;
       2:  
       3: import java.util.Iterator;
       4: import java.util.Random;
       5: import java.util.concurrent.CountDownLatch;
       6: import java.util.concurrent.DelayQueue;
       7: import java.util.concurrent.Delayed;
       8: import java.util.concurrent.TimeUnit;
       9:  
      10: /**
      11:  * Created with Simple_love
      12:  * Date: 2016/3/19.
      13:  * Time: 14:29
      14:  */
      15: public class Exam {
      16:         public static void main(String[] args) throws InterruptedException {
      17:                 int count = 20;
      18:                 DelayQueue<Student> students = new DelayQueue<>();
      19:                 Random random = new Random();
      20:                 CountDownLatch countDownLatch = new CountDownLatch(count+1);
      21:                 for (int i = 1; i <= 20; i++)
      22:                         students.add(new Student("student" + i,30+random.nextInt(120),countDownLatch));
      23:                 Thread teacherThread = new Thread(new Teacher(students));
      24:                 students.add(new EndExam(students,120,countDownLatch,teacherThread));
      25:                 teacherThread.start();
      26:                 countDownLatch.await();
      27:                 System.out.println(" 考试时间到,全部交卷!");
      28:         }
      29: }
      30: class Student implements Runnable,Delayed{
      31:         private String name;
      32:         private long workTime; //考试时间
      33:         private long submitTime; //交卷时间
      34:         private boolean isForce = false;
      35:         private CountDownLatch countDownLatch;
      36:  
      37:         public Student() {
      38:         }
      39:  
      40:         public Student(String name, long workTime,CountDownLatch countDownLatch) {
      41:                 this.name = name;
      42:                 this.workTime = workTime;
      43:                 this.submitTime = TimeUnit.NANOSECONDS.convert(workTime,TimeUnit.NANOSECONDS) + System.nanoTime();
      44:                 this.countDownLatch = countDownLatch;
      45:         }
      46:  
      47:         @Override
      48:         public long getDelay(TimeUnit unit) {
      49:                 return unit.convert(submitTime-System.nanoTime(),TimeUnit.NANOSECONDS);
      50:         }
      51:  
      52:         @Override
      53:         public int compareTo(Delayed o) {
      54:                 if (o == null || ! (o instanceof Student))
      55:                         return 1;
      56:                 if (o == this)
      57:                         return 0;
      58:                 Student temp = (Student)o;
      59:                 if (this.workTime > temp.workTime)
      60:                         return 1;
      61:                 else if (this.workTime == temp.workTime)
      62:                         return 0;
      63:                 else
      64:                         return -1;
      65:         }
      66:  
      67:         @Override
      68:         public void run() {
      69:                 if (isForce) {
      70:                         System.out.println(name + " 交卷, 希望用时" + workTime + "分钟"+" ,实际用时 120分钟" );
      71:                 }else {
      72:                         System.out.println(name + " 交卷, 希望用时" + workTime + "分钟"+" ,实际用时 "+workTime +" 分钟");
      73:                 }
      74:                 countDownLatch.countDown();
      75:         }
      76:  
      77:         public boolean isForce() {
      78:                 return isForce;
      79:         }
      80:  
      81:         public void setForce(boolean isForce) {
      82:                 this.isForce = isForce;
      83:         }
      84: }
      85:  
      86: class Teacher implements Runnable{
      87:  
      88:         private DelayQueue<Student> students;
      89:  
      90:         public Teacher(DelayQueue<Student> students) {
      91:                 this.students = students;
      92:         }
      93:  
      94:         @Override
      95:         public void run() {
      96:                 try {
      97:                         System.out.println("exam start");
      98:                         while (!Thread.interrupted())
      99:                                 students.take().run();
     100:                 }catch (InterruptedException e){
     101:                         System.out.println("exam end");
     102:                 }
     103:         }
     104: }
     105:  
     106: class EndExam extends Student{
     107:         private DelayQueue<Student> students;
     108:         private CountDownLatch countDownLatch;
     109:         private Thread teacher;
     110:  
     111:         public EndExam(DelayQueue<Student> students,long workTime,CountDownLatch countDownLatch,Thread teacher){
     112:                 super("强制收卷",workTime,countDownLatch);
     113:                 this.students = students;
     114:                 this.countDownLatch = countDownLatch;
     115:                 this.teacher = teacher;
     116:         }
     117:  
     118:         @Override
     119:         public void run() {
     120:                 teacher.interrupt();
     121:                 System.out.println("强制收卷");
     122:                 Student temp;
     123:                 for (Iterator<Student> iterator = students.iterator();iterator.hasNext();){
     124:                         temp = iterator.next();
     125:                         temp.setForce(true);
     126:                         temp.run();
     127:                 }
     128:                 countDownLatch.countDown();
     129:         }
     130: }

    四. Semaphore

    Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

    Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。

       1: public class SemaphoreTest {
       2:  
       3:      public static void main(String[] args) {  
       4:         // 线程池 
       5:         ExecutorService exec = Executors.newCachedThreadPool();  
       6:         // 只能5个线程同时访问 
       7:         final Semaphore semp = new Semaphore(5);  
       8:         // 模拟20个客户端访问 
       9:         for (int index = 0; index < 20; index++) {
      10:             final int NO = index;  
      11:             Runnable run = new Runnable() {  
      12:                 public void run() {  
      13:                     try {  
      14:                         // 获取许可 
      15:                         semp.acquire();  
      16:                         System.out.println("Accessing: " + NO);  
      17:                         Thread.sleep((long) (Math.random() * 10000));  
      18:                         // 访问完后,释放 ,如果屏蔽下面的语句,则在控制台只能打印5条记录,之后线程一直阻塞
      19:                         semp.release();  
      20:                     } catch (InterruptedException e) {  
      21:                     }  
      22:                 }  
      23:             };  
      24:             exec.execute(run);  
      25:         }  
      26:         // 退出线程池 
      27:         exec.shutdown();  
      28:     }  
      29: }
  • 相关阅读:
    虚拟机安装RHEL8.0.0
    给KVM添加新的磁盘
    RedHat7.4安装在个人电脑(笔记本)中安装遇到的问题总结
    shell编程-ssh免交互批量分发公钥脚本
    Error:Connection activation failed: No suitable device found for this connection 问题最新解决方案
    Linux下系统防火墙的发展历程和怎样学好防火墙(iptalbes和firewalld)
    Linux bash命令行常用快捷键(Xshell和secure CRT以及gnome-terminal)
    编写mysql多实例启动脚本
    RHEL7配置端口转发和地址伪装
    java中的事务,四大特性,并发造成的问题,隔离级别
  • 原文地址:https://www.cnblogs.com/bloodhunter/p/5295642.html
Copyright © 2020-2023  润新知