JAVA多线程系列--并发工具类(CountDownLatch, CyclicBarrier, Semaphore,Exchanger)

来源:互联网 发布:武汉理工网络教学平台 编辑:程序博客网 时间:2024/05/16 11:01

前言

  本节笔者将详细讲下CountDownLatch, CyclicBarrier, Semaphore,Exchanger 这四个并发工具类的使用。
  这4个工具类在高并发的场景下,也是使用广泛。

1.1 CountDownLatch简介

  CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

1.2 CountDownLatch使用场景

    1.实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。    2.开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。    3.死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。

1.3CountDownLatch 例子

/** * CountDownLatch * @author niyuelin * */public class CountDownLatchDemo {    final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    public static void main(String[] args) throws InterruptedException {        CountDownLatch latch = new CountDownLatch(2);// 两个工人的协作        Worker worker1 = new Worker("zhang san", 5000, latch);        Worker worker2 = new Worker("li si", 1000, latch);        worker1.start();//        worker2.start();//        latch.await();// 等待所有工人完成工作        System.out.println("all work done at " + sdf.format(new Date()));    }    static class Worker extends Thread {        String workerName;        int workTime;        CountDownLatch latch;        public Worker(String workerName, int workTime, CountDownLatch latch) {            this.workerName = workerName;            this.workTime = workTime;            this.latch = latch;        }        public void run() {            System.out.println("Worker " + workerName + " do work begin at " + sdf.format(new Date()));            doWork();// 工作了            System.out.println("Worker " + workerName + " do work complete at " + sdf.format(new Date()));            latch.countDown();// 工人完成工作,计数器减一        }        private void doWork() {            try {                Thread.sleep(workTime);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

1.4 输出结果

Worker li si do work begin at 2017-12-14 18:52:46Worker zhang san do work begin at 2017-12-14 18:52:46Worker li si do work complete at 2017-12-14 18:52:47Worker zhang san do work complete at 2017-12-14 18:52:51all work done at 2017-12-14 18:52:51

2.1 CyclicBarrier简介

  CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
  CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

2.2 CyclicBarrier使用场景

    CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景

2.3 CyclicBarrier例子

/** * CyclicBarrier * @author niyuelin * */public class CyclicBarrierDemo2 {    public static void main(String[] args) {        int N = 4;        CyclicBarrier barrier = new CyclicBarrier(N, new Runnable() {            @Override            public void run() {                System.out.println("当前线程" + Thread.currentThread().getName());            }        });        for (int i = 0; i < N; i++)            new Writer(barrier).start();    }    static class Writer extends Thread {        private CyclicBarrier cyclicBarrier;        public Writer(CyclicBarrier cyclicBarrier) {            this.cyclicBarrier = cyclicBarrier;        }        @Override        public void run() {            System.out.println("线程" + Thread.currentThread().getName() + "正在写入数据...");            try {                Thread.sleep(5000); // 以睡眠来模拟写入数据操作                System.out.println("线程" + Thread.currentThread().getName() + "写入数据完毕,等待其他线程写入完毕");                cyclicBarrier.await();            } catch (InterruptedException e) {                e.printStackTrace();            } catch (BrokenBarrierException e) {                e.printStackTrace();            }            System.out.println("所有线程写入完毕,继续处理其他任务...");        }    }}

2.4 输出结果

线程Thread-0正在写入数据...线程Thread-1正在写入数据...线程Thread-3正在写入数据...线程Thread-2正在写入数据...线程Thread-3写入数据完毕,等待其他线程写入完毕线程Thread-0写入数据完毕,等待其他线程写入完毕线程Thread-1写入数据完毕,等待其他线程写入完毕线程Thread-2写入数据完毕,等待其他线程写入完毕当前线程Thread-1所有线程写入完毕,继续处理其他任务...所有线程写入完毕,继续处理其他任务...所有线程写入完毕,继续处理其他任务...所有线程写入完毕,继续处理其他任务...

3.1 Semaphore简介

  Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
  Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

3.2 Semaphore使用场景

    1.流量控制,特别公用资源有限的应用场景,比如数据库连接    2.对某组资源的访问权限

3.3 Semaphore例子

/** * Semaphore * @author niyuelin * */public class SemaphoreTest {    private static final int THREAD_COUNT = 30;    private static ExecutorService executorService = Executors.newFixedThreadPool(30);    private static Semaphore s = new Semaphore(10);    private static int in = 0;    public static synchronized void add(){        in++;    }    public static void main(String[] args) throws InterruptedException {        for(int i =0; i<THREAD_COUNT; i++){            executorService.execute(new Runnable() {                public void run() {                    try {                        s.acquire();                        Thread.sleep(1000);                        add();//                      System.out.println("save data");                        s.release();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        }        Thread.sleep(1500);        System.out.println(in);        Thread.sleep(1000);        System.out.println(in);        executorService.shutdown();    }}

3.4 输出结果

1029

4.1 Exchanger简介

  Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

4.2 Exchanger使用场景

    1.遗传算法,遗传算法里需要选出两个人作为交配对象,这时候会交换两人的数据,并使用交叉规则得出2个交配结果。    2.校对工作 我们需要将纸制银流通过人工的方式录入成电子银行流水,为了避免错误,需要两个线程相互校对。

4.3 Exchanger例子

/** * Exchanger * @author niyuelin * */public class ExChangerTest {    private static final Exchanger<String> exgr = new Exchanger<String>();    private static ExecutorService executorService = Executors.newFixedThreadPool(2);    public static void main(String[] args) {        executorService.execute(new Runnable() {            @Override            public void run() {                try {                    String A = "银行流水A";                    String c = exgr.exchange(A);                    System.out.println("c: "+ c);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });        executorService.execute(new Runnable() {            public void run() {                try {                    String B = "银行流水B";                    String A = exgr.exchange(B);                    System.out.println("A:"+A+" ,B:"+B);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });        executorService.shutdown();    }}

4.4 输出结果

c:银行流水BA:银行流水A ,B:银行流水B
阅读全文
0 0