实战java高并发程序设计之CountDownLatch源码分析

版权声明:转载注明出处 https://blog.csdn.net/nobody_1/article/details/83006302

上篇文章讲到线程和进程的概念,并结合源码理解线程的各种状态,包括创建过程init(),启动过程start(),运行run(),退出exist(),等待和阻塞等。这篇博文主要讲JUC包中常用的并发工具类:CountDownLatch,CyclicBarrier,ReadWriteLock,Semaphore。

首先看第一个!

CountDownLatch

使用场景

CountDownLatch类是常见的并发同步控制类,适用于某一线程的执行在其他多个线程执行完成之后,比如火箭发射前需要各项指标检查,只有当各项指标检查完才能发射,再比如解析多个excel文档,只有当各个excel解析完成后,才能进行汇总。

代码实例

main线程代表点火,thread1/thread2/thread3分别表示三个检查指标,三个线程执行完成之后,点火线程通过await()方法判断三个线程是否执行成功,成功则继续执行点火线程,否则继续等待。

public class CountDownLatchTest {
	
	static final CountDownLatch cd = new CountDownLatch(3);  // 需要等待3个线程

	static class CheckOne implements Runnable {
		@Override
		public void run() {
			try {
				Thread.sleep(2000);   // sleep模拟检查
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cd.countDown();   // 该线程执行完,cd计数器减1
			System.out.println(Thread.currentThread().getName() + " :End!");
		}
	}
	
	static class CheckTwo implements Runnable{
		@Override
		public void run() {
			try {
				Thread.sleep(2000);  // sleep模拟检查
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cd.countDown();   // 该线程执行完,cd计数器减1
			System.out.println(Thread.currentThread().getName() + " :End!");
		}
	}
	
	static class CheckThree implements Runnable{
		@Override
		public void run() {
			try {
				Thread.sleep(2000);   // sleep模拟检查
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cd.countDown();    // 该线程执行完,cd计数器减1
			System.out.println(Thread.currentThread().getName() + " :End!");
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread thread1 = new Thread(new CheckOne(), "thread1");
		thread1.start();
		
		Thread thread2 = new Thread(new CheckTwo(), "thread2");
		thread2.start();
		
		Thread thread3 = new Thread(new CheckThree(), "thread3");
		thread3.start();
		
		cd.await();    // 判断cd计数器是否为0,否则继续等待
		System.out.println(Thread.currentThread().getName() + ": ok!");
	}
}

执行结果:

thread1 :End!
thread2 :End!
thread3 :End!
main: ok!
源码分析

构造方法:

public CountDownLatch(int count);  // count表示计数器
public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // 调用内部类Sync的构造方法
        this.sync = new Sync(count);
    }
    
Sync(int count) {
      setState(count);
}

CountDownLatch内部类Sync继承自AbstratcQueuedSynchronizer;所以其同步性利用AQS(AbstratcQueuedSynchronizer)框架实现。AQS内部维持一个volatile修饰的int类型的state;此处构造的参数count即为state赋值;

比较重要的方法有:

public void countdown();  // 调用一次 计数器减1
public void countDown() {
    sync.releaseShared(1);  // 实则调用sync的tryReleaseShared方法
}
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;   // 对status值减1
                if (compareAndSetState(c, nextc))  // 通过原子操作把改变后的status值写入内存中
                    return nextc == 0;
            }
        }

某线程执行完调用countDown(),表示所等待的线程数减1;

public void await() throws InterruptedException; // 等待的线程调用await,判断计数器是否为0
public void await() throws InterruptedException {
     sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();   // 如果调用await的线程被阻塞,则抛出异常
        if (tryAcquireShared(arg) < 0)    // status值为0,则等待结束,否则继续执行
            doAcquireSharedInterruptibly(arg);   // 继续阻塞线程
}
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;   // 根据status值是否为0,返回1或者-1
}

很明显,调用await()方法的线程通过判断state的值是否为零选择执行还是阻塞;

public boolean await(long timeout,TimeUnit unit) throws InterruptedException; //
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout);
    }

此方法与await()类似,只不过等待有限;若到达等待时间status值不为0则直接执行不等待;

注意事项

CountDownLatch需要明确等待的条件,即确定参数值。

参考资料

《实战java高并发程序设计》
博客1:https://blog.csdn.net/LightOfMiracle/article/details/73456832
博客2:http://www.importnew.com/21889.html

猜你喜欢

转载自blog.csdn.net/nobody_1/article/details/83006302