CountDownLatch

一、CountDownLatch简介

CountDownLatch是一个辅助同步器类,用来作计数使用,它的作用有点类似于生活中的倒数计数器,先设定一个计数初始值,当计数降到0时,将会触发一些事件,如火箭的倒数计时。

初始计数值在构造CountDownLatch对象时传入,每调用一次 countDown() 方法,计数值就会减1。

线程可以调用CountDownLatch的await方法进入阻塞,当计数值降到0时,所有之前调用await阻塞的线程都会释放。
注意:CountDownLatch的初始计数值一旦降到0,无法重置。如果需要重置,可以考虑使用CyclicBarrier。

二、CountDownLatch使用示例

1、作为一个开关/入口

package com.liaoxiang.multithreading3.middle.aqs_lock;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest2 {

    public static void main(String[] args) {
        CountDownLatch switcher = new CountDownLatch(1);

        for (int i = 0; i < 3; ++i) {
            new Thread(new Worker(switcher)).start();
        }
        doSomething();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行完成");
        switcher.countDown();       // 主线程开启开关
    }

    public static void doSomething() {
        System.out.println("主线程任务执行中...");
    }
}

class Worker implements Runnable {
    private final CountDownLatch startSignal;

    public Worker(CountDownLatch startSignal) {
        this.startSignal = startSignal;
    }

    @Override
    public void run() {
        try {
            System.out.println("进入子线程");
            startSignal.await();    //所有执行线程在此处等待开关开启
            doWork();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    void doWork() {
        System.out.println("子线程开始执行任务...");
    }
}

在这里插入图片描述

2、作为一个完成信号

package com.liaoxiang.multithreading3.middle.aqs_lock;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest1 {

	public static void main(String[] args) {
		
		final CountDownLatch countDown = new CountDownLatch(2);
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("t1开始执行,等待其他线程处理完成...");
					//线程执行到这里时阻塞,当有2个线程发出countDown.countDown()通知,此线程就继续
					countDown.await();
					System.out.println("t1线程继续执行...");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("t2线程进行初始化操作...");
					Thread.sleep(2000);
					System.out.println("t2线程初始化完毕,通知t1线程继续");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("t3线程进行初始化操作...");
					Thread.sleep(4000);
					System.out.println("t3线程初始化完毕,通知t1线程继续");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		t1.start();
		t2.start();
		t3.start();
	}
}

在这里插入图片描述

原理分析

可以看到CountDownLatch的实现同样是基于AQS,但是相比其他的同步器要简单很多
在这里插入图片描述

构造函数

从构造函数中可以看出,在创建CountDownLatch实例的时候,初始化了state状态变量,可以猜到本类中的await()countDown()方法,就是在对state进行加减的过程中判断state的状态来最终达到CountDownLatch所要实现的功能

//自身构造器
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
//内部类构造器
Sync(int count) {
    setState(count);
}

await()

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

//AbstractQueuedSynchronizer#acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted()) // 若线程中端,直接抛异常
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
    	//staste不为 0时,执行(tryAcquireShared(arg)返回false)
        doAcquireSharedInterruptibly(arg);
}

//CountDownLatch.Sync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
    // 计数state不为0时,返回-1
    return (getState() == 0) ? 1 : -1;
}

// AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED); // 入队
    boolean failed = true;
    try {
        for (;;) {
            // 获取前驱节点
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    // 获取锁成功,设置队列头为node节点
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node)
              && parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

countDown()

public void countDown() {
    sync.releaseShared(1);
}

//AbstractQueuedSynchronizer#releaseShared
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

//CountDownLatch.Sync#tryReleaseShared
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState(); //获取state
        if (c == 0) //如果state已经为0,则返回false
            return false;
        int nextc = c-1; //从当前值中-1
        if (compareAndSetState(c, nextc))//CAS设置-1后的新值
        	//如果设置成功后state为0,返回ture,否在还是返回false
        	//即执行一次countDown可能还不足以达到阻塞线程执行的条件
            return nextc == 0;
    }
}

// AbstractQueuedSynchronizer#doReleaseShared
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) { 
            // 头结点如果为SIGNAL,则唤醒头结点下个节点上关联的线程,并出队
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)){
             	continue;      // loop on failed CAS
            } 
        }
        if (h == head) // 没有线程被阻塞,直接跳出
            break;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值