并发工具类 countDownLatch、CyclicBarrier与Semaphore

1、等待多线程完成的 CountDownLatch

 CountDownLatch允许一个或多个线程等待其他线程完成操作。

它的构造函数接受一个int类型的参数作为计数器,如果想等待N个点完成,这里传入N即可。

调用countDown方法时,N就会减1,await方法会阻塞当前线程直到N为0时。N个点可以是N个线程,也可以是同一线程中的N个点。

比如如下代码实现等待N个线程执行并发并等待N个线程结束。

package cn.zhjw.eurekaclientnode2.common;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

/**
 * @Desc:
 * @Author: zhaojiwei
 * @Date: 2018/10/29 22:56
 */
public class ThreadSyn {

    CountDownLatch latchOne = new CountDownLatch(1);
    CountDownLatch latchTen = new CountDownLatch(10);

    public static void main(String[] args) throws IOException {
        ThreadSyn threadSyn = new ThreadSyn();
        threadSyn.testCountDownLatch();

        System.in.read();
        System.out.println("##############");
    }

    public void testCountDownLatch() {

        int m = 10;
        for (int i = 0; i < m; i++) {
            Thread thread = new Thread(new MyRunnable(latchOne, latchTen));
            thread.start();
        }
        System.out.println("开始并发执行。。。");
        latchOne.countDown();
        try {
            latchTen.await();
            System.out.println("############### All Thread Done!!#################");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    private class MyRunnable implements Runnable {

        private CountDownLatch countDownLathchOne;
        private CountDownLatch countDownLatchTen;

        public MyRunnable(CountDownLatch latchOne, CountDownLatch latchTen) {
            this.countDownLatchTen = latchTen;
            this.countDownLathchOne = latchOne;
        }

        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            try {
                System.out.println("到达等待点 。。。" + Thread.currentThread().getName());
                countDownLathchOne.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("running 。。。" + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //执行后减去信号量
            countDownLatchTen.countDown();
            
        }
    }

}

2、同步屏障 CyclicBarrier

顾名思义:可循环使用的屏障。它所要做的事情是让一组线程到达一个屏障(同步点)时阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被拦截的线程才会继续运行。

CyclicBarrier 与 CountDownLatch的区别:

  • CountDownLatch 计数器只能使用一次
  • CyclicBarrier 的计数器可通过方法reset() 重置,适合处理更复杂的业务场景;比如计算错误可以重置计数器重新计算一次;
  • CyclicBarrier 还有其他可用方法,比如getNumberWating() 可以忽的阻塞的线程数,isBorken()用来了解阻塞的线程是否被中断。

比如多线程汇总数据后,计算总数据。

package cn.zhjw.eurekaclientnode2.common;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @Desc:循环屏障
 * @Author: zhaojiwei
 * @Date: 2018/11/1 15:52
 */
public class ThreadSynCyclicBarrier {

    static final int COUNT = 10;
    /**
     * 假设需要执行4个任务,开启4个线程去执行
     */
    private Executor executor = Executors.newFixedThreadPool(COUNT);
    private ConcurrentHashMap<String, Integer> bankWaterCount = new ConcurrentHashMap<>();
    /**
     * 循环屏障,都到达同步点时,优先执行回调操作barrierAction
     */
    private CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT, new BankWaterService(bankWaterCount));



    public static void main(String[] args) {
        ThreadSynCyclicBarrier cb = new ThreadSynCyclicBarrier();
        cb.count();
        System.out.println("$#################");
    }

    /**
     * 模拟多线程计算
     */
    public void count() {
        for (int i = 0; i < COUNT; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    //计算当前sheet中银流数据,计算代码省略
                    bankWaterCount.put(Thread.currentThread().getName(), Integer.valueOf(1));
                    //银流计算完后,插入一个屏障
                    try {
                        System.out.println(Thread.currentThread().getName()+" => await...");
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    class BankWaterService implements Runnable {
        private ConcurrentHashMap<String, Integer> bankWaterCount;

        public BankWaterService(ConcurrentHashMap<String, Integer> bankWaterCount) {
            this.bankWaterCount = bankWaterCount;
        }

        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            int result = 0;
            Set<Map.Entry<String, Integer>> entries = bankWaterCount.entrySet();
            for (Map.Entry<String, Integer> entry : entries) {
                result += entry.getValue();
            }

            //将结果输出
            bankWaterCount.put("result", result);
            System.out.println(result);
        }


    }


}

3、控制并发量的信号量 Semaphore

Semaphore 信号量用于控制多线程最大并发度(并发数量)。它协调多线程合理利用公共资源。

Semaphore可用于流量的控制,特别是公共资源有限的应用场景,比如数据库连接。

package cn.zhjw.eurekaclientnode2.common;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @Desc: 信号量并发流量的限制
 * @Author: zhaojiwei
 * @Date: 2018/11/1 16:56
 */
public class ThreadSynSemaphore {

    private final static int THREAD_COUNT = 30;
    private final static int SEMAPHORE_COUNT = 10;

    /**
     * 30个线程
     */
    private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);

    /**
     * 10个信号量 控制并发度
     *
     * @param args
     */
    private static Semaphore semaphore = new Semaphore(SEMAPHORE_COUNT);

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println("save data!!");

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        }

        threadPool.shutdown();
    }
}

Semaphore 构造函数接受一个int类型的参数,表示可用的许可证,通过调研 acquire()获取一个许可证,用完后通过反复release() 归还许可证即可。还可以通过tryAcquire()方法尝试获取许可证。

4、线程间交换数据的 交换者 Exchanger

Exchanger 是用于线程间交换数据的工具类。它提供一个同步点,在这个同步点,两个线程可以交换彼此的 数据。

两个线程同步Exchanger交换数据,第一个线程如果先执行exchange()方法,它会一直等待第二个线程也执行exchange()方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产的数据传递给对方。

应用场景:

  • 遗传算法
  • 校对工作
package cn.zhjw.eurekaclientnode2.common;

import java.util.concurrent.*;

/**
 * @Desc: 线程间交换数据
 * @Author: zhaojiwei
 * @Date: 2018/11/1 17:15
 */
public class ThreadSynExchanger {

    /**
     * 交换字符串数据
     */
    private static final Exchanger<String> exchanger = new Exchanger<>();

    private static ExecutorService threadPool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                /**
                 * A 录入银行流水数据
                 */
                String A = "银行流水A";
                try {
                    String exchange = exchanger.exchange(A, 1, TimeUnit.MINUTES);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }

            }
        });

        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                /**
                 * B 员工录入的银行流水数据
                 */
                String B = "银行流水B";
                try {
                    String A = exchanger.exchange(B, 1, TimeUnit.MINUTES);
                    System.out.println("A和B的录入数据是否一致:" + A.equals(B) + ",A录入的是:" + A + ",B 录入的是:" + B);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

参考:《JAVA 并发编程的艺术》

猜你喜欢

转载自blog.csdn.net/manbufenglin/article/details/83621478