线程池
Java语言虽然内置了多线程支持,启动一个新线程非常方便,但是,创建线程需要操作系统资源(线程资源,栈空间等),频繁创建和销毁大量线程需要消耗大量时间
如果可以复用一组线程:
在没有任务时线程处于空闲状态,当请求到来:线程池给这个请求分配一个空闲的线程,任务完成后回到线程池中等待下次任务(而不是销毁)。这样就实现了线程的重用。
为每个请求都开一个新的线程虽然理论上是可以的,但是会有缺点:
- 线程生命周期的开销非常高。每个线程都有自己的生命周期,创建和销毁线程所花费的时间和资源可能比处理客户端的任务花费的时间和资源更多,并且还会有某些空闲线程也会占用资源。
- 程序的稳定性和健壮性会下降,每个请求开一个线程。如果受到了恶意攻击或者请求过多(内存不足),程序很容易就奔溃掉了。
所以说:我们的线程最好是交由线程池来管理,这样可以减少对线程生命周期的管理,一定程度上提高性能。
Executors 工厂类
前面已经整理了两个常见的创建方式:继承 Thread,重写Run() 方法 和 实现 Runnable 接口重写 run() 方法。
从 JDK1.5 开始,Java 提供 Callable 接口,提供了另一种方式:实现 Callable 接口创建线程,这个方式可以获取到线程执行的返回值、以及是否执行完成等信息。
在简单应用中前两种创建方式可以满足,但是在业务量大时,随着线程数量的增多,反而会耗尽 CPU 和内存资源,如果处理不当就会 OOM。因此就很有必要引入线程池
在线程频繁调度的场景中,JDK1.5 以前,必须手动打造线程池,来节约系统开销;而从 JDK1.5 开始,Java 提供了一个 Excutors 工厂类来生产线程池,进行线程控制
Executors 目前提供的 5 种不同的线程池创建配置:
newCachedThreadPool() 一种用来处理大量短时间工作任务的线程池
- 它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程
- 如果线程闲置的时间超过 60 秒,则被终止并移出缓存
- 长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列
newFixedThreadPool(int nThreads) 重用指定数目(nThreads)的线程
- 其背后使用的是无