为什么使用线程池
多线程场景下,线程的频繁创建和销毁,也就是频繁的进行上下文切换,消耗CPU资源,导致性能下降,为了解决这类问题,所以引入了线程池的概念,回收再利用已创建的线程资源
那么线程池具体是怎么工作的呢?
创建线程池
常规来讲,使用 Executors 能够快速的创建线程池,如下所示:
ExecutorService threadPool = Executors.newFixedThreadPool(1);
ExecutorService threadPool = Executors.newScheduledThreadPool(1);
ExecutorService threadPool = Executors.newSingleThreadExecutor();
ExecutorService threadPool = Executors.newCachedThreadPool();
但是结合阿里云的开发规范,不建议使用 Executors 的方式创建线程池,因为这样的处理方式无法让写代码的同学明确线程池的运行规则,从而规避资源耗尽的风险
Executors 方式的弊端
a) newFixedThreadPool 和 newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
b) newCachedThreadPool 和 newScheduledThreadPool:
主要问题是最大线程数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM
针对上述情况,业界建议使用 ThreadPoolExecutor 的方式创建线程池:
内部封装了不同参数个数的构造方法,这里以最全的构造方法为例
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
七个参数分别为:
核心线程数、最大线程数、最大空闲时间、空闲时间单位、阻塞队列、线程工厂、拒绝策略
使用示例如下:
public class TestThreadPoolExecutor {
private static ExecutorService threadPool = new ThreadPoolExecutor(2,
5,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue(1000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread("测试线程");
}
},
new ThreadPoolExecutor.DiscardPolicy()
);
public static void main(String[] args) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("哈哈哈");
}
});
}
}
工作原理
参数和使用方法知道了,那么 ThreadPoolExecutor 工作原理是怎样的呢?
线程池刚创建时,里面没有一个线程。
任务队列是作为参数传进来的,不过就算队列里有任务,线程池也不会立即执行它们,当调用 execute() 方法添加新任务时,线程池会做如下判断:
- 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务
- 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列
- 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么要创建非核心线程立刻运行这个任务
- 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException
- 当一个线程完成任务时,它会从队列中取下一个任务来执行
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉
综上所述,可知,线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小
以上就是个人理解的 线程池工作原理了
我是【辛勤de小蜜蜂】关注我,我们下期见
-
由于博主才疏学浅,难免会有纰漏,假如您发现了错误或遗漏的地方,还望留言斧正,我会尽快对其加以修正。
-
如果您觉得文章还不错,您的转发、分享、点赞、留言就是对我最大的鼓励。
-
感谢您的阅读,十分欢迎并感谢您的关注。