如何避免重复创建线程?创建线程池的方式有哪些?各自优缺点有哪些?
- 1、案例分析
- 2、创建线程池的方式
1、案例分析
我们在使用一些app的时候,应该都收到过消息推送,它们往往依赖消息推送服务实现。事实上,互联网大厂都有自己的消息推送服务(又名Message Push Server),通过消息推送服务实现对App的消息推送,不关注掉技术实现的细节,大致过程如图
如果让你来实现一个消息推送服务,要求保证消息推送的实时性,你会怎么设计呢?
你可能会想到,用多线程技术去做,为每个客户端都创建一个线程,然后启动线程,每来一条需要推送的消息就用创建好的线程发送出去。如图
每当有一个客户端与服务端建立连接,就创建一个线程来执行消息推送服务
这种方案看起来挺不错,但是可行性如何?其实,这种方案是无法在生产环境实战的。原因就在于不断创建线程这个操作本身是不合理的。
不慌,我们先看一段代码,在循环中一直创建线程,看看会出现什么情况,如图3所示。
可以看到控制台出现了java.lang.OutOfMemoryError报错,即内存已用尽。出现这个错误的原因就在于我们创建了太多的线程,线程对象本身以及线程的调用栈都是要占用内存的,而操作系统的内存是有限的,这决定了我们能创建的线程数也是有限制的,而无限制的创建线程会使内存不断消耗最终超过内存上限从而报错。
事实上,能创建多少线程数是有一个计算公式的:可创建的线程数 =(进程的最大内存 – JVM分配的内存 – 操作系统保留的内存)/ 线程栈大小。如图所示,粉红色的部分就是可分配线程的内存大小,如果不显式设置-Xss或-XX:ThreadStackSize参数的时候,在Linux x64上ThreadStackSize的默认值就是1024K,也就是1MB大小,如图