J.U.C之CountDownLatch

来源:互联网 发布:moedaze 优化 编辑:程序博客网 时间:2024/05/16 05:03

J.U.CCountDownLatch

一、CountDownLatch介绍

CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程等待。

用给定的计数初始化CountDownLatch。由于调用了CountDown方法,所以在当计数达到0之前,await方法会一直阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。

CountDownLatch经典的做法是两种:

1. 作为开关:

l 初始化计数器为1,作为一个主子线程的开关。

l 初始化计数器为n,主线程等待n个子线程完成任务后再执行后续的工作

2. 拆分子任务

        将一个问题分成 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。当所有的子部分完成后,协调线程就能够通过 await

二、实例

开关的使用:

package com.mylearn.thread;

import com.mylearn.util.DateUtil;

import java.util.Date;

import java.util.concurrent.CountDownLatch;

/**

 * Created by IntelliJ IDEA.

 * User: yingkh

 * Date: 12-12-20

 * Time: 上午10:59

 * CopyRight:360buy

 * Descrption:    CountDownLatch 类的使用

 * To change this template use File | Settings | File Templates.

 */

public class CountDownLatchDemo {

    public static void main(String args[]) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(2);  //保证子线程完毕后主线程继续工作

        CountDownLatch countDownLatchMain = new CountDownLatch(1);  //保证子线程同时开始工作

        Worker worker1 = new Worker("zhangsna", 5000, countDownLatch,countDownLatchMain);

        Worker worker2 = new Worker("lisi", 8000, countDownLatch,countDownLatchMain);

        worker1.start();

        worker2.start();

        doSth();

        countDownLatchMain.countDown();//在 countDownLatchMain计数器减1之前,子线程是阻塞的

        countDownLatch.await();//等待所有工人完成工作,主线程才能继续工作

        doSthElse();

        System.out.println("all work complete at" + DateUtil.date2String(new Date(), DateUtil.DATE_FORMAT_1));

    }

    private static void doSthElse() {

        try {

            System.out.println("主线程再休息一秒");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

        }

    }

    private static void doSth() {

        try {

            System.out.println("主线程休息一秒");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

        }

    }

    static class Worker extends Thread {

        String workerName;

        int workTime;

        CountDownLatch countDownLatch;

        CountDownLatch countDownLatchMain;

        public Worker(String workerName, int workTime, CountDownLatch countDownLatch, CountDownLatch countDownLatchMain) {

            this.workerName = workerName;

            this.workTime = workTime;

            this.countDownLatch = countDownLatch;

            this.countDownLatchMain = countDownLatchMain;

        }

        public void run() {

            try {

                countDownLatchMain.await();

            } catch (InterruptedException e) {

                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

            }

            System.out.println("Worker " + workerName + " do work begin at" + DateUtil.date2String(new Date(), DateUtil.DATE_FORMAT_1));

            doWork();     //工作

            System.out.println("Worker " + workerName + " do work complete at" + DateUtil.date2String(new Date(), DateUtil.DATE_FORMAT_1));

            countDownLatch.countDown(); //完成工作,计数器减1

        }

        private void doWork() {

            try {

                Thread.sleep(workTime);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

结果:

主线程休息一秒

Worker zhangsna do work begin at2013-11-10 08:53:06

Worker lisi do work begin at2013-11-10 08:53:06

Worker zhangsna do work complete at2013-11-10 08:53:11

Worker lisi do work complete at2013-11-10 08:53:14

主线程再休息一秒

all work complete at2013-11-10 08:53:15

第二种用法:

package com.mylearn.thread;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

 * Created by IntelliJ IDEA.

 * User: yingkh

 * Date: 13-11-10

 * Time: 上午8:56

 * CopyRight:360buy

 * Descrption:

 * CountDownLatch的第二种使用,通过

 * To change this template use File | Settings | File Templates.

 */

public class CountDownLatch2Demo {

    public static void main(String args[]) {

        int n=10;

        CountDownLatch countDownLatch =new CountDownLatch(n);

        ExecutorService executorService = Executors.newCachedThreadPool();

        for(int i=0;i<n;i++) {

            Woker woker =new Woker(i,countDownLatch);

            executorService.submit(woker);     //调用不同的worker工作

        }

        try {

            countDownLatch.await();

            System.out.println("end");

        } catch (InterruptedException e) {

            e.printStackTrace();  

        }

    }

    static class Woker implements  Runnable{

        int i;

        CountDownLatch countDownLatch;

        Woker(int i, CountDownLatch countDownLatch) {

            this.i = i;

            this.countDownLatch = countDownLatch;

        }

        public void run() {

            dowWork(i);  //根据参数调用做不同的事

            countDownLatch.countDown();

        }

        private void dowWork(int i) {  //模拟分页

            int pageSize = 100;

            int begin = i*  100;

            int end = begin+99;

            System.out.println("worker is running between ["+ begin +"] with [" + end+"].");

        }

    }

}

三、源码分析

Await:

public void await() throws InterruptedException {

        sync.acquireSharedInterruptibly(1);

    }

acquireSharedInterruptibly:

    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {

        if (Thread.interrupted())

            throw new InterruptedException();

        if (tryAcquireShared(arg) < 0)

            doAcquireSharedInterruptibly(arg);

    }

tryAcquireShared:

        public int tryAcquireShared(int acquires) {

            return getState() == 0? 1 : -1; //

        }

countDownLatchtryAcquireShared方法非常简单,只是判断计数是否为0;如果为0,线程不受影响,如果大于0,需要阻塞线程。doAcquireSharedInterruptiblyAQS公用的阻塞方法,在前几篇中分享过,此处略。

countDown

    public void countDown() {

        sync.releaseShared(1);

    }

releaseShared

    public final boolean releaseShared(int arg) {

        if (tryReleaseShared(arg)) { //返回true之后,唤醒阻塞队列

            Node h = head;

            if (h != null && h.waitStatus != 0)

                unparkSuccessor(h);

            return true;

        }

        return false;

    }

tryReleaseShared

        public boolean tryReleaseShared(int releases) {

            // Decrement count; signal when transition to zero

            for (;;) {

                int c = getState();

                if (c == 0)

                    return false;

                int nextc = c-1; 

                if (compareAndSetState(c, nextc))

                    return nextc == 0; //只有减为0时会返回true

            }

        }

    }

countDown操作很简单,仅仅是更新计数器,通过CAS,减一;如果减为0的时候唤醒阻塞队列

原创粉丝点击