CountDownLatch是(算)什么东西?

文章目录

  • 前言
  • 使用方式
  • 二、源码解析
    • 1.内部类Sync
    • 主要方法
      • countDown()
      • await()
  • 总结


前言

CountDownLatch字面意思是倒计时门闩,是基于AQS共享锁实现的,主要有两个方法,countDown方法和await方法。创建CountDownLatch时会指定count数量,count不为0时调用await方法线程会阻塞,每调用一次countDown方法会将count减1,直到count==0时await方法才会解除阻塞。

使用方式

先来看看使用方法,主要有以下两种:
第一种:
主线程等待子线程执行完任务再往下执行,伪代码如下

class Driver2 {
      // ...
   	public static void main(String[] args) {
     
    	CountDownLatch doneSignal = new CountDownLatch(N);
    	Executor e = Executors.newFixedThreadPool(10);
	    for (int i = 0; i < N; ++i) // create and start threads
	       	e.execute(new WorkerRunnable(doneSignal, i));
	     	doneSignal.await();           // wait for all to finish
   		}
 	}
}
class WorkerRunnable implements Runnable {
     
	private final CountDownLatch doneSignal;
   	private final int i;
   	WorkerRunnable(CountDownLatch doneSignal, int i) {
     
     	this.doneSignal = doneSignal;
    	this.i = i;
  	}
   	public void run() {
     
     	try {
     
	      doWork(i);
	      doneSignal.countDown();
     	} catch (InterruptedException ex) {
     } // return;
  	}

   	void doWork() {
      ... }
}

第二种:
多个子线程等待主线程的发号施令,主线程等待子线程完成后往下执行,伪代码如下:

class Driver {
     
   	public static void main(String[] args) {
     
	    CountDownLatch startSignal = new CountDownLatch(1);
	    CountDownLatch doneSignal = new CountDownLatch(N);

    	for (int i = 0; i < N; ++i) // create and start threads
       		new Thread(new Worker(startSignal, doneSignal)).start();

     	doSomethingElse();            // don't let run yet
   		startSignal.countDown();      // let all threads proceed
    	doSomethingElse();
    	doneSignal.await();           // wait for all to finish
 	}
}
class Worker implements Runnable {
     
   	private final CountDownLatch startSignal;
   	private final CountDownLatch doneSignal;
  	Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     
	    this.startSignal = startSignal;
	    this.doneSignal = doneSignal;
  	}
   	public void run() {
     
		try {
     
		      startSignal.await();
		      doWork();
		      doneSignal.countDown();
	     } catch (InterruptedException ex) {
     } // return;
   	}
  	void doWork() {
      ... }
}

二、源码解析

1.内部类Sync

既然是基于AQS实现,那么就必须继承AQS,实现对应的方法,然后再确定AQS中的state属性在该实现类中代表什么含义。在CountDownLatch中,state代表的含义就是count。

// 先看看构造方法
public CountDownLatch(int count) {
     
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

private static final class Sync extends AbstractQueuedSynchronizer {
     
    Sync(int count) {
     
        setState(count); // state即为count
    }

    int getCount() {
     
        return getState();
    }
	// 获取锁,count为0获取锁成功,不为0获取锁失败
    protected int tryAcquireShared(int acquires) {
     
        return (getState() == 0) ? 1 : -1;
    }
	// 释放锁,调用一次count减1,如果count==0返回释放成功,否则失败
    protected 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;
        }
    }
}

主要方法

countDown()

AQS方法,释放锁。会调用上面实现的tryReleaseShared,如果该方法返回true,则会唤醒因获取该锁而阻塞的线程,并且可以往后传播。
将count减1,如果count==0则唤醒所有await等待线程,因为是共享锁,可以传播。

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

await()

AQS方法,获取锁,获取失败则阻塞。会调用上面实现的tryAcquireShared至少一次。

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

总结

基于AQS实现的CountDownLatch是非常简单的,在此也不得不感叹AQS同步框架的厉害之处和所处的核心地位。还是那句话,想要基于AQS实现同步器,只要明确了state属性的含义,再实现对应的方法就可以了。阅读基于AQS实现的同步器我们只需要找准state的含义和阅读对应方法的实现就能明白这个同步器实现原理了。AQS懂了同步器这一块基本上就没啥问题了。

你可能感兴趣的:(JDK源码系列,java,多线程,并发编程)