原创

Java并发编程(三)-CountDownLatch和CyclicBarrier

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

CountDownLatch

       CountDownLatch类利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
       CountDownLatch类只提供了一个构造器:

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

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

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

       使用案例:

public class TestCountDownLatch {
    
        public static void main(String str[]){
            try {
                int threadNum=4;
                final CountDownLatch latch = new CountDownLatch(threadNum);
                for(int i=1;i<=threadNum;i++){
                    new Thread(new Job("线程:"+i,latch)).start();
                }
                System.out.println("等待子线程...");
                latch.await();
                System.out.println("子线程执行完毕...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    class Job implements Runnable {
        
        private String threadName;
    
        private  CountDownLatch latch;
    
        Job( String name,CountDownLatch latch) {
            this.threadName = name;
            this.latch=latch;
            System.out.println("创建线程:" +  threadName );
        }
        @Override
        public void run() {
            try {
                System.out.println("运行线程:" +  threadName );
                Thread.sleep(2000);
                System.out.println("结束线程:" +  threadName);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                latch.countDown();
            }
        }
    }

       运行结果

创建线程:线程:1
创建线程:线程:2
创建线程:线程:3
创建线程:线程:4
等待子线程...
运行线程:线程:3
运行线程:线程:1
运行线程:线程:4
运行线程:线程:2
结束线程:线程:3
结束线程:线程:1
结束线程:线程:4
结束线程:线程:2
子线程执行完毕...

       从上面结果可以看出,只有在所有子线程结束完成后,latch.await()之后主线程才会继续运行。

CyclicBarrier

       字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:

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

       参数parties指让多少个线程或者任务等待至barrier(障碍)状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:
       第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务。

public int await() throws InterruptedException, BrokenBarrierException { };

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

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

       使用案例:

public class TestCyclicBarrier {

    static private CyclicBarrier cyclicBarrier;
    public static void main(String str[]){
        int threadNum=4;
        cyclicBarrier=new CyclicBarrier(threadNum);
        for(int i=1;i<=threadNum;i++){
            new Thread(new CyclicBarrierJob("线程:"+i,cyclicBarrier)).start();
        }

    }
    static class CyclicBarrierJob implements Runnable {
        private String threadName;
        private CyclicBarrier cyclicBarrier;
        CyclicBarrierJob( String name,CyclicBarrier cyclicBarrier) {
            this.threadName = name;
            this.cyclicBarrier=cyclicBarrier;
            System.out.println("创建" +  threadName );
        }
        @Override
        public void run() {
            try {
                System.out.println( threadName+"正在处理任务");
                //暂停2秒,模拟完成任务所需时间
                Thread.sleep(2000);
                System.out.println(threadName+"完成任务");
                cyclicBarrier.await();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("其他线程已经完成任务," +  threadName+"结束线程");
        }
    }
}

       执行结果:

创建线程:1
创建线程:2
创建线程:3
线程:1正在处理任务
创建线程:4
线程:4正在处理任务
线程:3正在处理任务
线程:2正在处理任务
线程:1完成任务
线程:4完成任务
线程:3完成任务
线程:2完成任务
其他线程已经完成任务,线程:1结束线程
其他线程已经完成任务,线程:4结束线程
其他线程已经完成任务,线程:3结束线程
其他线程已经完成任务,线程:2结束线程

       从上面输出结果可以看出,每个线程执行完2s任务之后,就在等待其他线程执行任务完毕。当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。
       如果想在所有线程写入操作完之后,进行额外的其他操作可以使用构造方法public CyclicBarrier(int parties, Runnable barrierAction)为CyclicBarrier提供Runnable参数:

public class TestCyclicBarrier {
    static private CyclicBarrier cyclicBarrier;
    public static void main(String str[]){
        int threadNum=4;
        cyclicBarrier  = new CyclicBarrier(threadNum,new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程是:"+Thread.currentThread().getName());
            }
        });
        for(int i=1;i<=threadNum;i++){
            new Thread(new CyclicBarrierJob("线程:"+i,cyclicBarrier)).start();
        }

    }
    static class CyclicBarrierJob implements Runnable {

        private String threadName;
        private CyclicBarrier cyclicBarrier;

        CyclicBarrierJob( String name,CyclicBarrier cyclicBarrier) {
            this.threadName = name;
            this.cyclicBarrier=cyclicBarrier;
            System.out.println("创建" +  threadName );
        }
        @Override
        public void run() {
            try {

                System.out.println( threadName+"正在处理任务");
                //暂停2秒,模拟完成任务所需时间
                Thread.sleep(2000);
                System.out.println(threadName+"完成任务");
                cyclicBarrier.await();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("其他线程已经完成任务," +  threadName+"结束线程");
        }
    }
}

       当四个子线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。
       public int await(long timeout, TimeUnit unit)设置超时等待时间,如:

cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);

       等待2秒后其他子线程还没有完成任务就抛出异常并继续执行下去。CyclicBarrier重用:
当初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用。而CountDownLatch无法进行重复使用。

总结

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

正文到此结束
Loading...