项目场景:
在项目开发工程中,多少会遇到使用线程池的场景。
实现功能需要记录子线程执行结果
问题描述
实际执行过程中,发现主线程未等待,就执行完成了。
代码实现:
public void test1() throws InterruptedException {
System.out.println("test ------------1111----------------------------");
long startTime = System.currentTimeMillis();
List<String> jhh = new ArrayList<>();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(15, 15, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
//源数据大概 8000条
List<String> list = testDao.getList();
for (String zf : list) {
threadPool.execute(new Runnable() {
@Override
public void run() {
ReturnValue audit = auditService.audit(zf);
if (audit.getShow()) {
jhh.add(zf);
log.error("计划号:"+zf+",失败:"+audit.getShow());
}
}
}
);
}
long endTime = System.currentTimeMillis();
long usedTime = (endTime - startTime) / 1000;
System.out.println("-----------总计耗时" + usedTime + "-------------");
System.out.println("数量:" + jhh.size());
System.out.println(jhh);
原因分析:
threadPool 未设置线程等待时间。
解决方案:
实现方式1 :
threadPool.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS);
该方法调用会被阻塞,并且在以下几种情况任意一种发生时都会导致该方法的执行: 即shutdown方法被调用之后,或者参数中定义的timeout时间到达或者当前线程被打断,这几种情况任意一个发生了都会导致该方法在所有任务完成之后才执行。第一个参数是long类型的超时时间,第二个参数可以为该时间指定单位。
public void test1() throws InterruptedException {
System.out.println("test ------------1111----------------------------");
long startTime = System.currentTimeMillis();
List<String> zfjhh = new ArrayList<>();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(15, 15, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
List<String> list = payoutStatusDao.getList();
for (String zf : list) {
threadPool.execute(new Runnable() {
@Override
public void run() {
ReturnValue audit = auditService.audit(zf);
if (audit.getShow()) {
zfjhh.add(zf);
log.error("计划号:"+zf+",失败:"+audit.getShow());
}
}
}
);
}
//设置主线程等待时间
threadPool.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS);
long endTime = System.currentTimeMillis();
long usedTime = (endTime - startTime) / 1000;
System.out.println("-----------总计耗时" + usedTime + "-------------");
System.out.println("数量:" + zfjhh.size());
System.out.println(zfjhh);
实现方式2(推荐使用) :
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
public void test1() throws InterruptedException {
System.out.println("test ------------1111----------------------------");
long startTime = System.currentTimeMillis();
List<String> zfjhh = new ArrayList<>();
List<String> list = payoutStatusDao.getList();
// 使用线程池中线程分批处理业务逻辑,并行处理任务提高终端响应速度
CountDownLatch latch = new CountDownLatch(list.size());
for (String zf : list) {
ThreadUtils.execute(() -> {
try {
// 业务逻辑正常处理......
ReturnValue audit = auditService.audit(zf);
if (audit.getShow()) {
zfjhh.add(zf);
log.error("计划号:" + zf + ",失败:" + audit.getShow());
}
} catch (Exception e) {
log.error("调用下游系统出现错误,异常逻辑处理......");
} finally {
// 业务逻辑处理完毕,计数器减一【当前线程处理任务完毕,线程释放进入线程池,等待处理下一个任务】
latch.countDown();
}
});
}
// 主线程需要等待子任务线程执行完,结果汇总之后,主线程继续往下执行
try {
latch.await();
} catch (Exception e) {
log.error("等待超时", e);
throw new RuntimeException("系统处理超时,请稍后再试");
}
long endTime = System.currentTimeMillis();
long usedTime = (endTime - startTime) / 1000;
System.out.println("-----------总计耗时" + usedTime + "-------------");
System.out.println("数量:" + zfjhh.size());
System.out.println(zfjhh);