java实现多个线程达到一个阙伐值后一起执行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yueloveme/article/details/83655837

给大家推荐个靠谱的公众号程序员探索之路,大家一起加油

1. CountDownLatch
1.1 简介
CountDownLatch是一个同步辅助类,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。
1.2 API
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
用指定的值初始化计数器。

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

调用该方法的线程进入等待状态,直到计数器的值减至0或者该线程被其他线程Interrupted。

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

调用该方法的线程进入等待状态,直到计数器的值减至0或者该线程被其他线程Interrupted或者等待时间超过指定的时间。

    public void countDown() {
        sync.releaseShared(1);
    }

减少计数器当前的值,每次调用值减少1。

    public long getCount() {
        return sync.getCount();
    }

获取计数器当前的值
1.3 使用场景
在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作;典型的应用如并行计算,当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。

public static void main(String[] args) throws InterruptedException {
        Runnable taskTemp = new Runnable() {
            private int num = 0;
            @Override
            public void run() {
                for (int i = 0;i < 10;i++){
                    HttpUtil.getURLContent("https://www.baidu.com/");
                        num++;
                        System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + num);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
//        Thread t = new Thread(){
//            public void run(){
//                taskTemp.run();
//            }
//        };
//        t.start();
        Test test = new Test();
        test.startTaskAllInOnce(5, taskTemp);
//        test.startNThreadsByBarrier(5, taskTemp);
    }

    public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(threadNums);
        for(int i = 0; i < threadNums; i++) {
            Thread t = new Thread() {
                public void run() {
                    try {
                        // 使线程在此等待,当开始门打开时,一起涌入门中
                        startGate.await();
                        try {
                            task.run();
                        } finally {
                            // 将结束门减1,减到0时,就可以开启结束门了
//                            endGate.countDown();
                        }
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            };
            t.start();
        }
        long startTime = System.nanoTime();
        System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");
        // 因开启门只需一个开关,所以立马就开启开始门
        startGate.countDown();
        // 等等结束门开启
        endGate.await();
        long endTime = System.nanoTime();
        System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");
        return endTime - startTime;
    }

2. CyclicBarrier
2.1 简介
CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。
2.2 API
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

用指定值和Runnable初始化CyclicBarrier

    public CyclicBarrier(int parties) {
        this(parties, null);
    }

用指定值初始化CyclicBarrier

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen;
        }
    }

调用该方法的线程进入等待状态,并且计数器加1,直到调用该方法的线程数达到设置值后或该线程被其他Interrputed

    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

调用该方法的线程进入等待状态,并且计数器加1,直到调用该方法的线程数达到设置值后或该线程被其他Interrputed或者等待时间超过指定时间。

    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

判断该Barrier是否处于broker状态

    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

重置barrier进入初始状态。
2.3  使用场景
使用场景类似于CountDownLatch
2.4  与CountDownLatch的区别
CountDownLatch主要是实现了1个或N个线程需要等待其他线程完成某项操作之后才能继续往下执行操作,描述的是1个线程或N个线程等待其他线程的关系。CyclicBarrier主要是实现了多个线程之间相互等待,直到所有的线程都满足了条件之后各自才能继续执行后续的操作,描述的多个线程内部相互等待的关系。
CountDownLatch是一次性的,而CyclicBarrier则可以被重置而重复使用。

public static void main(String[] args) throws InterruptedException {
        Runnable taskTemp = new Runnable() {
            private int num = 0;
            @Override
            public void run() {
                for (int i = 0;i < 10;i++){
                    HttpUtil.getURLContent("https://www.baidu.com/");
                        num++;
                        System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + num);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
//        Thread t = new Thread(){
//            public void run(){
//                taskTemp.run();
//            }
//        };
//        t.start();
        Test test = new Test();
//        test.startTaskAllInOnce(5, taskTemp);
        test.startNThreadsByBarrier(5, taskTemp);
    }
public void startNThreadsByBarrier(int threadNums, Runnable finishTask) throws InterruptedException {
        // 设置栅栏解除时的动作,比如初始化某些值  注意 这里的finishTask执行的 时间是 该CyclicBarrier达到阙伐值后的回调的一个任务
        CyclicBarrier barrier = new CyclicBarrier(threadNums, finishTask);
        // 启动 n 个线程,与栅栏阀值一致,即当线程准备数达到要求时,栅栏刚好开启,从而达到统一控制效果
        for (int i = 0; i < threadNums; i++) {
//            Thread.sleep(100);
            new Thread(new CounterTask(barrier)).start();
        }
        System.out.println(Thread.currentThread().getName() + " out over...");
    }
    class CounterTask implements Runnable {

        private CyclicBarrier barrier;

        public CounterTask(final CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        public void run() {
            System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready...");
            try {
                // 设置栅栏,使在此等待,到达位置的线程达到要求即可开启大门
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started...");
        }
    }

猜你喜欢

转载自blog.csdn.net/yueloveme/article/details/83655837
今日推荐