采用闭锁(CountDownLatch)控制线程的先后顺序(一)

  CountDownLatch俗称闭锁,可以初始化一个值,每执行一次countDown()操作,值减一,减为0时,在闭锁前等待的线程即可执行,但是闭锁的值不能恢复,即一次性。下面是api文档中的介绍:

  A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

  可以应用在一个线程等待其他事件发生后,再继续向下操作的场景。CyclicBarrier是栅栏,可以等待所有的线程到达后,一同干下面的事,这在另一种应用场景,在下一篇中有介绍。看CountDownLatch实例:

package com.smikevon.concurrent.tools;

import java.util.concurrent.CountDownLatch;

/**
 * @description: 要求:妈妈炒菜,但是这个时候还没买菜,也没有酱油了。为了更快的吃上饭,妈妈,小明,小强,分别行动,
 *                  妈妈负责敲响做饭铃(铃响之前不许动),兄弟二人就可以出发,然后妈妈收拾厨房调好料,小明负责买菜,小强负责买酱油,协作完成此项工作
 *
 *                  CountDownLatch 保证了其他线程执行完,才开始主线程.
 *                  特点:所有的其他线程执行完,执行目标线程(即执行了await的线程)
 * @date       : 2014年9月17日 下午2:41:50
 */
public class TestCountDownLatch_01 {

    public static void main(String[] args) {

        //响铃的次数
        final int BellRingCount = 1;
        //需要买菜的次数
        final int MaterialCount = 1;
        //需要买酱油的次数
        final int SoysauceCount = 1;

        final CountDownLatch bellRingLatch = new CountDownLatch(BellRingCount);
        final CountDownLatch buyMaterialLatch = new CountDownLatch(MaterialCount);
        final CountDownLatch buySoySauceLath = new CountDownLatch(SoysauceCount);

        new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("准备做饭了哦!");
                    System.out.println("ding ding ding !!!");

                    /**
                     * 这条代码和下面的顺序颠倒就会导致死锁
                     */
                    bellRingLatch.countDown();
                    //等待菜和酱油都买回来了
                    buyMaterialLatch.await();
                    buySoySauceLath.await();

                    System.out.println("我是妈妈,菜和酱油都齐了,我开始做饭喽!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        },"妈妈").start();

        //小明等待铃响就去买菜
        new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("我是小明,我准备去买菜(to go)!");
                    bellRingLatch.await();
                    System.out.println("我是小明,我已经买到了菜(get back)!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                buyMaterialLatch.countDown();
            }
        },"小明").start();

        new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("我是小强,我准备去打酱油(to go)!");
                    bellRingLatch.await();
                    System.out.println("我是小强,我已经打到了酱油(get back)!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                buySoySauceLath.countDown();
            }
        },"小强").start();

    }

}

这里每个线程都持有一个闭锁,初始值为1,实际上这样做有些繁琐,因为小明和小强的行为模式一致,都是等待妈妈发出号令后出发,都是跑回来以后,妈妈才开始做饭,因此可以将两个闭锁合二为一。详情请看下一篇

原文地址:https://www.cnblogs.com/seanvon/p/4072072.html