美文网首页
JAVA并发编程(三):同步的辅助类之闭锁(CountDownL

JAVA并发编程(三):同步的辅助类之闭锁(CountDownL

作者: CatalinaX | 来源:发表于2018-08-25 14:01 被阅读0次
    volatile_logo

    jdk1.5的concurrent包为我们提供了很多有用的辅助类,今天我们来学习一下

    一、闭锁CountDownLatch

    CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。它的作用是,在完成某些运算时,只有其他所有线程的运算全部完成,当前线程的运算才继续执行。
    CountDownLatch类只提供了一个构造器:

    public CountDownLatch(int count) {  };  //参数count为计数值
    

    然后下面这3个方法是CountDownLatch类中最重要的方法:

    public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
    public void countDown() { };  //将count值减1
    

    接下来我们看看下面的这个例子:

    public class TestCountDownLatch {
    
        public static void main(String[] args) {
            final CountDownLatch latch = new CountDownLatch(50);
            LatchDemo ld = new LatchDemo(latch);
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < 50; i++) {
                new Thread(ld).start();
            }
    
            try {
                latch.await();
            } catch (InterruptedException e) {
            }
    
            long end = System.currentTimeMillis();
    
            System.out.println("耗费时间为:" + (end - start));
        }
    
    }
    
    class LatchDemo implements Runnable {
    
        private CountDownLatch latch;
    
        public LatchDemo(CountDownLatch latch) {
            this.latch = latch;
        }
    
        @Override
        public void run() {
    
            try {
                for (int i = 0; i < 50000; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            } finally {
                latch.countDown();
            }
    
        }
    
    }
    

    通过CountDownLatch,我们就可以计算一批线程的执行时间。从这个例子中我们也可以看出:CountDownLatch中维护了一个int类型的变量。当前线程调用await()方法来使用这个辅助类,在这个变量成为0之后当前线程就会继续执行下去了。所以我们在构造器里填入的变量要跟等待的线程数量相同。

    二、CyclicBarrier

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

    CyclicBarrier提供2个构造器:

    public CyclicBarrier(int parties, Runnable barrierAction) {
    }
     
    public CyclicBarrier(int parties) {
    }
    

    参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
      然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:

    public int await() throws InterruptedException, BrokenBarrierException { };
    public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
    

    第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;

    第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。

    public class Test {
        public static void main(String[] args) {
            int N = 4;
            CyclicBarrier barrier  = new CyclicBarrier(N);
            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("所有线程写入完毕,继续处理其他任务...");
            }
        }
    }
    

    执行结果

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

    CyclicBarrier和CountDownLatch的区别

    • CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器是可以重用的。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
    • CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

    参考文章


    本文作者: catalinaLi
    本文链接: http://catalinali.top/2018/helloLatch/

    相关文章

      网友评论

          本文标题:JAVA并发编程(三):同步的辅助类之闭锁(CountDownL

          本文链接:https://www.haomeiwen.com/subject/onqgiftx.html