最近在项目中使用CountDownLatch和Future开发,经过并发测试,发现有些数据结果并没有按照期望获取到,这是为什么呢?
jdk1.8.0_112
代码如下(涉及到项目信息,以下代码是按照项目中抽取):
package me.qianlv;
import java.util.List;
import java.util.concurrent.*;
/**
* CountDownLatch 测试
*
* @author xiaoshu
*/
public class CountDownLatchTwo {
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
testResult();
}
}
public static void testResult() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Future<String> one = executorService.submit(() -> {
try {
return "ONE";
} finally {
latch.countDown();
}
});
Future<String> two = executorService.submit(() -> {
try {
return "TWO";
} finally {
latch.countDown();
}
});
Future<String> three = executorService.submit(() -> {
try {
return "Three";
} finally {
latch.countDown();
}
});
latch.await(1000, TimeUnit.MILLISECONDS);
//项目中,通过判断线程是否执行完成,获取线程结果
if (!one.isDone()) {
System.out.println(" 线程1没有运行完");
}
if (!two.isDone()) {
System.out.println(" 线程2没有运行完");
}
if (!three.isDone()) {
System.out.println(" 线程3没有运行完");
}
}
}
通过1000次循环请求模拟,输出结果如下:
预期的应该是,所有的线程都运行结果,不应该有任何输出,但是,从结果来看,确实有输出!!!
后来分析原因,突然想到了,finally其实是在return之前执行!!!也就是说,当CountDownLatch解锁的时候,return语句可能未被执行。
使用总结:
使用CountDownLatch、CyclicBarrier这类控制并发等待的工具类时,不应该采用Future返回值的方式。
可采用线程外传对象,修改对象的方式。如:
package me.qianlv;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* CountDownLatch 测试
*
* @author xiaoshu
*/
public class CountDownLatchTwo {
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
testResult();
}
}
public static void testResult() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Result oneResult = new Result();
executorService.execute(() -> {
try {
oneResult.setMessage("ONE");
} finally {
latch.countDown();
}
});
Result twoResult = new Result();
executorService.execute(() -> {
try {
twoResult.setMessage("TWO");
} finally {
latch.countDown();
}
});
Result threeResult = new Result();
executorService.execute(() -> {
try {
threeResult.setMessage("Three");
} finally {
latch.countDown();
}
});
latch.await(1000, TimeUnit.MILLISECONDS);
//项目中,通过判断线程是否执行完成,获取线程结果
if (null == oneResult.getMessage()) {
System.out.println("线程1没有设置值");
}
if (null == twoResult.getMessage()) {
System.out.println("线程2没有设置值");
}
if (null == threeResult.getMessage()) {
System.out.println("线程3没有设置值");
}
}
}
class Result {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
后者使用ExecutorService.submit(Runnable task, T result)
方法,该方法比较简单,不再演示了。