Java线程池使用不当会发生什么-生产案例
线程池使用不当会发生什么?如下图所示(可以自己先分析):
这个是我公司在实际工作中发现的一个问题。但是这个接口调用频率不高所以在线上并没有发现问题(发版重新发布项目就会释放服务器的资源)
将线程池化的目的就是减少线程创建和销毁的资源消耗,同时也防止用户无限制的创建线程把系统资源全部消耗掉。所以Java定义了线程池提供给开发者使用。但是如果线程池使用不当会发生什么呢?这种代码写在这里分分钟四十米的大刀架在脖子上。吐槽完,我们接下来写一个类似的来看看会发生什么以及分析一下导致这种情况发生的原因以及如何规避。
1. 例子演示
演示代码地址:https://github.com/mxsm/spring-sample/tree/master/spring-boot-protobuf
定义一个Controller
@RestController
public class ThreadPoolController {
@Autowired
private ThreadPoolService threadPoolService;
@GetMapping(value = "/createThreadPool")
public boolean createThreadPool(){
return threadPoolService.createThread();
}
}
定义一个ThreadPool的Service,模拟上图的问题
@Service
public class ThreadPoolService {
public boolean createThread(){
//增加ThreadFactory为了好区分
ExecutorService executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
/**
* Constructs a new {@code Thread}. Implementations may also initialize priority, name, daemon
* status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to create a thread is rejected
*/
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "mxsm");
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("王尼玛");
}
});
return true;
}
}
然后通过调用接口 /createThreadPool
模拟上面工作中的真实实例的问题。看看会发生什么。将项目打包成jar包然后运行在本地。
然后运行项目:
运行后不停的请求上面的接口:
看到有执行的打印说明已经请求到了。然后我们通过命令: jps
以及 jstack -l <pid>
来查看线程池的情况
注意观察下线程的状态如下图:
都是在等待的过程中,这说明了什么问题?线程并没有随着方法执行完成而消亡
这个就很可怕了,如果你的接口不停的被调用,这个线程的创建数会源源不断的增加,当增加到你服务器资源全部被使用完就会导致服务宕机报(java.lang.OutOfMemoryError)。接下来分析如何为什么会写出这样的代码
Tips: 这里可能很多人没有发现 Executors.newSingleThreadScheduledExecutor 这个创建的是一个单线程的线程池。所以导致的线程增长速度是调用一次接口增加1,如果是100个线程线程池呢?那可能会很快导致服务不可用宕机。我只想说还好是单线程线程池接口调用频率不高