上篇文章讲到线程和进程的概念,并结合源码理解线程的各种状态,包括创建过程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