使用线程池的shutdown()使主线程等待子线程执行完所有任务是错误的

问题

public void test() {
	for (Data data : dataList) {
	  executorService.submit(() -> {
	     	handle(data);//执行业务代码
	   });
	}
	//关闭线程池
	repaireExecutorService.shutdown();
	try {
	  //想要让主线程等待子线程执行完
	  executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
	
	} catch (InterruptedException e) {
	   LOGGER.error("销毁修复任务线程失败!", e);
	   throw new IllegalStateException(e);
	}
}

结果

子线程可能执行不完所以的任务就会被关闭。shutdown源码,如下会先广播状态,并执行中断任务。在这个过程中子线程只可能完成手上的任务然后就被关闭,没法再去阻塞队列中取出任务。可以看interruptIdleWorkers()源码

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);//广播状态
        interruptIdleWorkers();//中断任务
        onShutdown();//默认是空方法
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

主线程给所有的子线程发送中断信号,而子线程当执行完手上的任务的时候就会先去检测是否已经收到了中断信号(其实就是一个布尔型的值,是否被设置了),如果已经检测到中断信号了,则不会去阻塞队列中取出新的任务,所以所有任务是不会执行完毕的。

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

解决方法

使用CountDownLatch

CountDownLatch有一个计数器,子线程负责将计数器减一,主线程负责调用await()等待,当某个子线程将计数器减为0的时候,就会唤醒主线程

public void test() {
	//创建dataList任务总数的countDownLatch计数器
	CountDownLatch countDownLatch = new CountDownLatch(dataList.size());        
	for (Data data : dataList) {
	   executorService.submit(() -> {
	   		try {
	   		   handle(data);//执行业务代码
	   		} finally {
	         countDownLatch.countDown();
	        }
	      	
	    });
	}
	try {
	    //等待计数器归零
	    countDownLatch.await();
	} catch (InterruptedException e) {
	    e.printStackTrace();
	}
}

使用Future.get()

Future.get()时是阻塞的,会等待子线程执行完毕主线程才能获取到结果,相当于主线程在等待该子线程。

public void test() {
	//创建Future<String>列表,用于存放执行完的结果获取器
	List<Future<String>>  list = new ArrayList();
	for (Data data : dataList) {
	   executorService.submit(() -> {
	        Future<String>> future = handle(data);//执行业务代码
	   		list.add(future);
	    });
	}
	
	for (Future<String> future : list) {
		String str = future.get();//在这里开始主线程就会进行等待
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中可以使用CountDownLatch(倒计时门闩)来实现等待指定数量的线执行毕后再执行主线程。 具体实现步骤如下: 1. 创建一个CountDownLatch对象,其参数为需要等待线程数量。 2. 在需要等待线程的run()方法中,执行任务后调用CountDownLatch的countDown()方法,表示该线程已任务。 3. 在主线程中调用CountDownLatch的await()方法,使主线等待所有线任务后再继续执行。 示例代码如下: ``` import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class WaitThreadPool { public static void main(String[] args) throws InterruptedException { int threadCount = 5; //线程数量 CountDownLatch latch = new CountDownLatch(threadCount); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); for(int i=0; i<threadCount; i++) { executorService.execute(new Task(latch)); } latch.await(); //等待所有线执行成 System.out.println("所有线执行成,主线程继续执行"); executorService.shutdown(); //关闭线程池 } static class Task implements Runnable { private CountDownLatch latch; public Task(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { //模拟线执行任务 Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + " 执行成"); } catch (InterruptedException e) { e.printStackTrace(); } finally { latch.countDown(); //任务执行成,计数器减1 } } } } ``` 在该示例中,创建了一个线程池并启动了5个线执行任务。每个线执行任务后调用CountDownLatch的countDown()方法,最终主线程调用CountDownLatch的await()方法等待所有线执行任务后再继续执行

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lolxxs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值