如何在Java中创建一个自定义的线程池?

上周,我的一个朋友在学习Java线程池时遇到了一些困惑。
他问我,创建自己的线程池基本上都是通过ThreadPoolExecutor类来完成的吧?
我告诉他是这样的。
首先,你要配置corePoolSize、MaximumPoolSize、KeepAliveTime等核心参数。
他问这些参数到底是什么意思,我解释道:

corePoolSize:即使空闲也不会被杀死的核心线程数量(除非设置为allowCoreThreadTimeOut)。

maximumPoolSize:最大线程数,线程池允许的最大并发线程数。

keepAliveTime:非主线程空闲超时。

unit:时间单位(例如,TimeUnit.SECONDS)。

workQueue:阻塞任务队列,存放需要执行的任务。

threadFactory(可选):用于创建您自己的线程的工厂。

handler:拒绝策略,线程池和队列满时处理新任务的逻辑。

然后他问我如何选择任务队列。
我举了一个例子:

LinkedBlockingQueue:无界或有界链表队列,适合任务量可预测的场景。

ArrayBlockingQueue:有界数组队列,必须提前指定容量,防止资源耗尽。

SynchronousQueue:不存储元素的队列。
每个插入操作都必须等待删除操作。
适用于高吞吐量场景。

选择退出政策也很关键。
我提醒他:

AbortPolicy(默认):抛出RejectedExecutionException。

CallerRunsPolicy:调用线程直接执行任务,降低调度率。

DiscardOldestPolicy:删除队列中最旧的任务并尝试重新发送它。

DiscardPolicy:静默丢弃任务而不抛出异常。

关闭线程池也很重要。
我教的:

shutdown():停止接受新任务,继续执行提交的任务。

shutdownNow():尝试停止所有任务并返回未完成任务的列表。

awaitTermination():等待任务完成或超时。

他还想查看完整的代码示例,所以我给了他一个:
java 导入 java.util.concurrent.;
公共类 CustomThreadPool { 公共静态无效主(字符串[] args){ // 1 .定义线程池参数 int 核心池大小 = 2 ; int 最大池大小 = 4 ; 长保持活动时间 = 6 0; 时间单位 = TimeUnit.SECONDS; BlockingQueue workQueue = new LinkedBlockingQueue(1 00); RejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
// 2 .创建线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, KeepAliveTime, unit, workQueue, handler);
// 3 . 提交任务 for (int i = 0; i < 1 xss=clean> { System.out.println("任务 " + TaskId + " 正在运行于 " + Thread.currentThread().getName()); 尝试{ 线程睡眠(1 000); // 模拟任务执行 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } });
// 4 .关闭线程池 执行器.shutdown(); 尝试{ if (!executor.awaitTermination(6 0, TimeUnit.SECONDS)) { 执行器.shutdownNow(); } } catch (InterruptedException e) { 执行器.shutdownNow(); } }
最后我提醒了他设置参数、队列容量、拒绝策略、资源释放等关键点。
正确配置线程池设置可以显着提高系统性能并避免资源浪费。
他点点头,说道:“谢谢,我明白了。

上海某小公司面试题:Java线程池来聊聊

Java的线程池是关于线程的复用,保存创建和销毁,控制数量防止爆炸。
需要明确的是,ThreadPoolExecutor 有四个主要组件:管理器、工作线程、任务队列和拒绝策略。

Builder方法参数都在这里了:核心线程数、最大线程数、空闲生存时间、队列类型、线程工厂和拒绝策略。
实现过程分为三个步骤:检查是否有足够的线程,不足则添加;当队列满时,查看是否有足够的线程,如果不够则添加;如果你不够好,请遵循拒绝政策。

你应该这样看参数:​​核心线程数即使空闲也可以运行,最大线程数即使满了也可以增加。
队列分为有限队列和无限队列。
无界队列很容易出现OOM。
有多种拒绝策略,默认的策略是抛出异常。

JDK给你提供了四个现成的线程池,但是不要盲目使用执行器,因为这样很容易搞砸。

监控指标全部在ThreadPoolExecutor中:主线程数、活动线程数、已完成作业数、队列大小。

最佳实践:如果 CPU 密集型,则根据 CPU 核心数进行分配。
如果IO集中,计算就复杂。
单独的任务必须链接起来,否则线程将被关闭。
使用 shutdown+waitTermination 来正常关闭。

询问者喜欢问:如何复用线程? ——检查主线程是否充足。
如果没有,请添加新任务。
为什么不使用执行器? ——默认队列可能会爆炸。
如何选择队列? ——取决于按需或实时性能。
明确地说,为了更深入地理解,您应该查看源代码,尤其是 addWorker 和 Worker 类。
建议对整个过程进行编辑和监控。