如何用Java的ScheduledThreadPoolExecutor重启终止线程并维持固定线程数?
嘿,我明白你想要的效果——一个固定线程数的“不死”线程池,不管线程是正常跑完还是意外挂掉,都能立刻补一个一模一样的上去,持续输出1-2-3-4循环。你现在代码跑一次就停,核心问题是普通线程池里的线程完成任务后就会终止,不会自动重启,而且如果你的任务本身只执行一次打印,那线程跑完自然就歇菜了。
给你两个可行的解决方案,按需选择:
方案一:让任务本身持续运行(最简单直接)
这种思路是让每个线程的任务变成无限循环,哪怕遇到异常也能继续执行,从根源上避免线程终止。代码示例:
import org.junit.Test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Testing { // 定义一个会重复执行的任务 class RepeatingTask implements Runnable { private final int number; public RepeatingTask(int number) { this.number = number; } @Override public void run() { // 只要线程没被主动中断,就一直循环执行 while (!Thread.currentThread().isInterrupted()) { try { System.out.print(number + "-"); Thread.sleep(1000); // 模拟任务耗时,可根据需求调整 } catch (InterruptedException e) { // 捕获中断信号,优雅退出循环 Thread.currentThread().interrupt(); break; } catch (Exception e) { // 捕获其他所有异常,防止线程意外挂掉 System.err.println("任务" + number + "出问题了:" + e.getMessage()); } } } } @Test public void testPersistentThreadPool() throws InterruptedException { int fixedThreadCount = 4; ExecutorService executor = Executors.newFixedThreadPool(fixedThreadCount); // 提交4个对应数字的任务 for (int i = 1; i <= fixedThreadCount; i++) { executor.submit(new RepeatingTask(i)); } // 让JUnit测试线程一直等待,否则测试结束会杀掉所有线程 Thread.currentThread().join(); } }
这个方案的好处是简单直接,每个线程会一直跑下去,除非被主动中断。就算某个任务抛出异常,也会被捕获,线程不会终止,依然能继续打印数字。
方案二:线程终止时自动补新线程(更灵活)
如果你的任务是一次性的,但希望线程跑完后立刻补一个相同的任务上去(比如任务是周期性执行,但不想用循环),可以自定义线程池,重写afterExecute方法来实现自动补位:
import org.junit.Test; import java.util.concurrent.*; public class Testing { class RepeatingTask implements Runnable { private final int number; public RepeatingTask(int number) { this.number = number; } @Override public void run() { System.out.print(number + "-"); // 这里可以是你的一次性任务逻辑 } } // 自定义线程池,实现线程终止后自动补位 class ReplacingThreadPool extends ThreadPoolExecutor { private final TaskFactory taskFactory; public ReplacingThreadPool(int coreSize, TaskFactory factory) { super(coreSize, coreSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); this.taskFactory = factory; } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); // 只要线程池没关闭,就提交一个和原任务一样的新任务 if (!isShutdown()) { submit(taskFactory.copyTask(r)); } } } // 任务复制工厂,用来创建和原任务一模一样的副本 interface TaskFactory { Runnable copyTask(Runnable original); } @Test public void testReplacingThreadPool() throws InterruptedException { int fixedThreadCount = 4; TaskFactory factory = original -> { // 复制原来的RepeatingTask if (original instanceof RepeatingTask) { return new RepeatingTask(((RepeatingTask) original).number); } return original; }; ReplacingThreadPool executor = new ReplacingThreadPool(fixedThreadCount, factory); // 初始化提交4个任务 for (int i = 1; i <= fixedThreadCount; i++) { executor.submit(new RepeatingTask(i)); } // 让测试线程持续等待 Thread.currentThread().join(); } }
这个方案里,每个任务执行完一次后,线程池会自动创建一个相同的任务提交进去,保证线程数量始终是4个,持续输出1-2-3-4循环。哪怕某个线程因为异常挂了,afterExecute依然会触发,补上新的任务和线程。
为什么你原来的代码会停?
大概率是你的任务只执行了一次打印就结束了,线程池里的线程完成任务后就进入空闲状态,核心线程默认会等待新任务,但你没有继续提交任务,所以整个程序就停了。上面的两种方案要么让任务自己循环跑,要么自动补新任务,就能解决这个问题。
内容的提问来源于stack exchange,提问作者psychorama
相关产品推荐
相关产品推荐

