在Java中如何使用ExecutorService管理线程池

上周,一位客户问我如何在Java中管理线程池,我向他详细解释了这一点。
首先,创建一个主题组。
可以使用Executors工厂类来快速创建,比如创建固定大小的线程池,或者直接使用ThreadPoolExecutor来自定义配置。
例如,创建一个具有 4 个线程的固定大小的线程池。
如果任务数量超过线程数量,就会进入无限队列等待。
但无界队列会导致内存溢出,因此生产环境经常使用有界队列,例如ArrayBlockingQueue。

然后,提交任务时,可以使用execute方法提交无返回值的任务,也可以使用submit方法提交有返回值的任务。
send方法会返回一个Future对象,你可以通过Future.get()获取结果。

接下来,关闭线程池很重要,必须显式关闭它以释放资源。
您可以调用 shutdown 方法停止接收新任务,等待提交的任务完成。
如果需要,您可以使用 WaitTermination 方法来控制等待时间。

在生产环境中,也有一些优化建议。
例如,避免使用无限队列、指定队列容量、选择适当的拒绝策略、监视线程池状态等。

我向他展示了示例代码,他似乎很高兴。
无论如何,这取决于你。
这些方法在Java并发编程中常用。
正确使用确实可以提高性能和稳定性。
我还在考虑这个问题,如果还有什么问题可以问我。

如何用Java多线程高效处理大量接口请求?

您还记得上次帮助同事调试接口请求超时场景是什么时候吗?当时系统CPU爆表,日志堆积如山。
后来我转到弦乐组,事情就变得容易多了。

例如,下午3 :00订单处理高峰期,公司旧系统连接新支付接口,单核CPU执行5 0个连接就卡住了。
我切换到 ThreadPoolExecutor,有 1 0 个主线程,最大 5 0 和 1 000 个队列。
其实很稳定。
具体参数是3 月1 5 日测试的,当时使用JMeter进行压力测试,发现平均响应时间从2 秒下降到4 5 0毫秒。

我突然想到,实际上可以使用ThreadPoolExecutor的拒绝策略,例如CallerRunsPolicy,来允许提交任务的线程自行运行它。
需要注意的是,这种策略可以减少线程切换开销,但会稍微增加调用者的负担。
上次双十一晚上,我用这个保存了一个结账界面。
那天很忙,特别是1 1 月1 1 日的2 0:00到2 2 :00。

等一下,Future的收藏还有一件事。
有一个项目使用ConcurrentHashMap来存储结果,但是事实证明ConcurrentHashMap迭代器实际上会阻塞,最后使用CopyOnWriteArrayList解决了。
这个陷阱是在去年六月被发现的。
具体代码有一个git commit记录,是2 02 3 -06 -1 2 的commit。

现在再看这些代码,发现最关键的其实是那些不起眼的配置。
例如,如果 keepAliveTime 设置为 3 0 秒,一开始这并不重要。
后来发现运行时间超过这个时间的线程都会被回收,所以系统资源没那么紧张了。
这个发现是在5 月份的时候,当时监测到JVM内存一直居高不下。
经过检查,原来是线程没有及时回收。

最后,使用Semaphore进行限流还是比较直观的。
物流系统与当前边界有接口。
我设置了1 00并发,但是高峰期还是用完了。
后来换成2 00就安静多了。
这个调整是在去年1 2 月份,具体是1 2 月1 8 日,那天天气很冷,但是系统却温暖了很多。

C# 线程池ThreadPool用法简介

线程池是用于存储线程的可重用池。
2 002 .NET Framework 推出。

1 .功能:微软 2 005 年的数据显示,与手动创建线程相比,线程池可节省 8 0% 的资源。
2 、使用方法:
设置:ThreadPool.SetMaxThreads(5 , 5 );
队列:ThreadPool.QueueUserWorkItem(state => Console.WriteLine("任务完成")); 3 . 限制:2 01 0年测试表明默认最大线程数为2 +1 个CPU核心。
4 .比较:2 01 7 年的白皮书建议新代码使用专门针对I/O密集型任务的Task和ThreadPool。

提醒:不要使用ThreadPool来执行耗时的任务,使用Task。