线程池拒绝策略

如何优雅的使用和理解线程池?你怎么看?

如何优雅的使用和理解线程池?你怎么看?

要把java线程池理解好并且用好,需要把并发编程的基础知识掌握好,并且把线程池的所有API的官方文档仔细阅读研究一遍。这里把优雅的使用线程池的要点总结如下:
1. 弄明白你要用线程池做什么。例如你的目的是把同步API改造为异步,还是想要并发请求多个外部服务,还是减少线程的创建和销毁以处理用户请求等。
2. 根据你的实际项目需求,配置好线程池的参数,具体包括corePoolSize, maximumPoolSize, 阻塞队列, keepAliveTime,线程工厂 和 饱和策略(或者说是RejectedExecutionHandler)。
3. 根据你的应用特点部署线程池。有的后台服务应用适合在启动的时候一次性创建好线程池,在应用的执行过程不再修改线程池。有的时候,应用适合临时创建一个线程池并且把任务提交进去,用完之后立即销毁。
4. 当你决定不再使用线程池之后,应该调用shutdown()以优雅的关闭线程池。shutdown可以保证之前已经提交到线程池中的任务不会被丢弃,保证了数据安全。
5. 当调用了shutdown之后,线程池此时已经可能在执行任务,只是关闭了提交任务的入口。如果需要等待线程池完全终止,需要调用awaitTerminate以等待线程池把队列中的任务全部处理完成并且清理完成,然后才返回。awaitTerminate成功返回了,线程池算是真的清理干净了。
总结一下,线程池按照创建(构造方法)、提交任务(execute)、清理(shutdown),等待清理结束(awaitTerminate)的顺序调用API,这样使用线程池才算优雅。要想用好java线程池,最大化优化程序的性能可以参考我公众号里的分析文章。

为什么Java坚持多线程不选择协程?

从java被发明的第一天起,就被定义为一个多线程的网络编程语言。Java最大特点并不是跨平台,而是它的多线程模型(那时候的C 中,并没有我们现在看到的thread,C#还没有出来)。因为近二十年的软件行业的增长主要来自网络编程,网络编程最常见的模型就是client/server, 也就是所谓的C/S,这种编程模型在服务器端需要同时接受客户端的请求,也就是说要有很好的并发特性--这个特性主要依赖多线程来实现。而java的主战场就是服务器端编程。所以多线程对java是极为重要,不可或缺的一环。
当我们希望引入协程,我们想解决什么问题。我想不外乎下面几点:
节省资源,轻量,具体就是:节省内存,每个线程需要分配一段栈内存,以及内核里的一些资源节省分配线程的开销(创建和销毁线程要各做一次syscall)节省大量线程切换带来的开销与NIO配合实现非阻塞的编程,提高系统的吞吐使用起来更加舒服顺畅(async await,跑起来是异步的,但写起来感觉上是同步的)我们分开来讲下。
先说内存。拿Java Web编程举例子,一个tomcat上的woker线程池的最大线程数一般会配置为50~500之间(目前springboot的默认值给的200)。也就是说同一时刻可以接受的请求最多也就是这么多。如果超过了最大值,请求直接打失败拒绝处理。假如每个线程给128KB,500个线程放一起的内存占用量大概是60 MB。如果真的有瓶颈,也许CPU,IO,带宽,DB的CPU等会有瓶颈,但这点内存量的增幅对于动辄数个GB的Java运行时进程来说似乎并不是什么大问题。