Java并发工具类CountDownLatch原理及使用

在JDK并发包里提供了几个非常有用的并发工具类。CountDownLath、CylicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了线程间交换数据的一种手段。

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

假如我们有这样一个需求:我们需要解析一个Excel的多个sheet的数据,此时我们可以考虑使用多线程,每个线程解析一个sheet的数据,等到所有线程都解析完之后,程序需要提示解析完成。

在这个需求中,要实现主线程等待所有线程完成sheet的解析操作,最简单的做法是使用join()方法,如下:

/**
 * @author perist
 * @date 2017/3/19
 * @time 18:00
 */
public class JoinCountDownLatchDemo {
public static void main(String[] args) throws InterruptedException{
    Thread parse1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("sheet 1 finished");
        }
    });

    Thread parse2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("sheet 2 finished");
        }
    });

    parse1.start();
    parse2.start();
    parse1.join();
    parse2.join();
    System.out.println("all finished ");
}

}

join可以让当前执行线程等待join线程执行结束。其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待,wait(0)表示永远等待。代码片段如下:

     while (isAlive()) {
                wait(0);
            }

直到线程join终止后,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM里实现的,所以在JDK中看不到。

在JDK中提供的CountDownLatch也可以实现join的功能,并且比join功能更多

public class CountDownLatchDemo {
public static void main(String[] args) {
    CountDownLatch countDownLatch = new CountDownLatch(3);
    CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo();
    Thread first = new Thread(countDownLatchDemo.new Service("first", 4, countDownLatch));
    Thread second = new Thread(countDownLatchDemo.new Service("second", 200, countDownLatch));
    Thread third = new Thread(countDownLatchDemo.new Service("third", 110, countDownLatch));
    first.setDaemon(true);
    first.start();
    second.start();
    third.start();
    try {
        countDownLatch.await();
        System.out.println("协同线程全部启动完毕。!");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }


}


/**
 * 线程服务类
 */
class Service implements Runnable {

    /**
     * 别名
     */
    private final String name;
    /**
     * 线程等待时间
     */
    private final int timeToStart;

    /**
     * 计数
     */
    private final CountDownLatch countDownLatch;

    public Service(String name, int timeToStart, CountDownLatch countDownLatch) {
        this.name = name;
        this.timeToStart = timeToStart;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(timeToStart);
            System.out.println(name + ":执行完毕");
            countDownLatch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}

 

CountDownLatch的构造函数接收一个int参数作为计数器,如果你想等待N个点完成,这里就传入N。

当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变成0。由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是一个线程里面的N个步骤。用在多线程时,只需要把这个CountDownLatch传入到线程里即可。

一个线程调用countDown方法happen-before,另一个线程调用await方法。

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x