更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页
本文将介绍什么是闭锁,在java中的闭锁实现:CountDownLatch类及其常用方法等,最后给出了一个使用闭锁模拟线程并发的demo,用以简单地测试任务是否为线程安全。
一、什么是闭锁
闭锁(Latch)是在并发编程中常被提及的概念。闭锁是一种线程控制对象,它能让所有的线程在某个状态时终止工作并等待,直到闭锁“开门”时,所有的线程在这一刻会几乎同时执行工作,制造出一个并发的环境。
二、CountDownLatch类介绍
2.1、什么是CountDownLatch
CountDownLatch,顾名思义,可以理解为计数(count)、减少(down)、闭锁(Latch),即通过计数减少的方式来达到阻碍线程执行任务的一种闭锁,这个类位于java.util.concurent并发包下,是java中闭锁的最优实现。
2.2、构造方法
CountDownLatch(int count)
构造器中计数值(count)就是闭锁需要等待的线程数量,这个值只能被设置一次。
2.3、主要方法
-
void await():
使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 -
boolean await(long timeout, TimeUnit unit):
使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 -
void countDown():
递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 -
long getCount():
返回当前计数。 -
String toString():
返回标识此锁存器及其状态的字符串。
三、使用闭锁完成并发测试
使用闭锁完成并发测试的基本思路是,定义一个startLatch闭锁,并且它的线程等待值设置为1;之后新建的每一个线程都需要在执行任务前都在这个startLatch下等待,等所有线程都已集合完毕后,释放startLatch,让所有的线程都执行任务。
- 测试非线程安全的程序:
private int count_unsafe = 0;
class MyTask_Unsafe implements Runnable {
@Override
public void run() {
count_unsafe++;
System.out.println(Thread.currentThread().getName() + "读数为" + count_unsafe);
}
}
@Test
public void testConcurrent() {
//新建闭锁
CountDownLatch startLatch = new CountDownLatch(1);
//模拟100个线程去并发执行任务
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
try {
//线程工作到此处即停止
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//在闭锁允许后,调用任务
new MyTask_Unsafe().run();
}
}.start();
}
//100次循环后,100个线程已经创建并且在等待中,可以统一开始执行任务
System.out.println("所有线程集合完毕,等待执行任务...");
//开始执行任务
startLatch.countDown();
}
}
执行结果为:
可以发现,有3个线程输出的数字都为7,原因在第一篇文章中已经讲过,count_unsafe++这个操作并不是原子的,而是存在读-改-写
三个过程,所以线程不安全,下面我们测试一下线程安全的任务:
private AtomicInteger count = new AtomicInteger(0);
class MyTask_Safe implements Runnable {
//线程安全类
@Override
public void run() {
count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + "读数为" + count);
}
}
@Test
public void testConcurrent() {
//新建闭锁
CountDownLatch startLatch = new CountDownLatch(1);
//模拟100个线程去并发执行任务
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
try {
//线程工作到此处即停止
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//在闭锁允许后,调用任务
new MyTask_Safe().run();
}
}.start();
}
//100次循环后,100个线程已经创建并且在等待中,可以统一开始执行任务
System.out.println("所有线程集合完毕,等待执行任务...");
//开始执行任务
startLatch.countDown();
}
}