这里是引用
一、什么是线程池
线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。
二、为什么要使用线程池
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建(new线程)和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
三、线程池
线程池的7大参数
//构造方法
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;
}
线程池的工作流程图
线程池的3大方法
1.Executors.newSingleThreadExecutor
核心思想:单个线程
newSingleThreadExecutor源码部分:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
//最大线程数和核心线程数都是1
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
代码部分:
public static void main(String[] args) {
//可以理解为单例
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i = 1;i<=10;i++){
int temp = i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
});
}
执行结果
pool-1-thread-1执行第1个任务
pool-1-thread-1执行第2个任务
pool-1-thread-1执行第3个任务
pool-1-thread-1执行第4个任务
pool-1-thread-1执行第5个任务
pool-1-thread-1执行第6个任务
pool-1-thread-1执行第7个任务
pool-1-thread-1执行第8个任务
pool-1-thread-1执行第9个任务
pool-1-thread-1执行第10个任务
2.Executors.newFixedThreadPool(int nThreads)
核心思想:创建一个固定的线程池大小
源码部分:
public static ExecutorService newFixedThreadPool(int nThreads) {
//最大线程数和核心线程数由程序员自己设置
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
代码部分:
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 1;i<=10;i++){
int temp = i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
});
}
}
结果:
pool-1-thread-1执行第1个任务
pool-1-thread-5执行第5个任务
pool-1-thread-4执行第4个任务
pool-1-thread-3执行第3个任务
pool-1-thread-2执行第2个任务
pool-1-thread-3执行第9个任务
pool-1-thread-4执行第8个任务
pool-1-thread-5执行第7个任务
pool-1-thread-1执行第6个任务
pool-1-thread-2执行第10个任务
3.Executors.newCachedThreadPool()
核心思想:可伸缩的,遇强则强,遇弱则弱
源码部分:
public static ExecutorService newCachedThreadPool() {
//核心线程数是0,最大线程数是Integer.MAX_VALUE
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
//当线程执行完任务闲置久了就会被淘汰
//设置了线程的淘汰时间
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
代码部分:
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 1;i<=100;i++){
int temp = i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行第"+ temp+"个任务");
});
}
}
部分结果:
pool-1-thread-1执行第1个任务
pool-1-thread-2执行第2个任务
pool-1-thread-3执行第3个任务
pool-1-thread-5执行第5个任务
pool-1-thread-6执行第6个任务
pool-1-thread-4执行第4个任务
pool-1-thread-8执行第8个任务
pool-1-thread-7执行第7个任务
pool-1-thread-9执行第9个任务
pool-1-thread-4执行第14个任务
pool-1-thread-7执行第12个任务
pool-1-thread-8执行第13个任务
可以从结果看出第7,8个线程去执行了多次任务,当任务执行完毕后,根据线程池设置线程最大的闲置时间,把线程回收掉。
线程池的4大拒绝策略
new ThreadPoolExecutor.AbortPolicy(); // 抛出异常
new ThreadPoolExecutor.CallerRunsPolicy();// 哪来的去哪(主线程来的,就回去让主线程执行)
new ThreadPoolExecutor.DiscardPolicy();// 丢掉任务,不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();//阻塞队列满了,把最前面阻塞队列的任务丢掉,并执行当前任务,也不会抛出异常!
1.new ThreadPoolExecutor.AbortPolicy()
自己创建一个线程池:
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,//核心线程数
5,//最大线程数
3,//最长闲置时间
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),//最多有3个任务队列
Executors.defaultThreadFactory(),
//拒接策略:抛出异常
new ThreadPoolExecutor.AbortPolicy()
);
for(int i = 1;i<=8;i++){
//使用了线程池之后,使用线程池来创建线程
int temp = i;
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
});
}
}
八个任务正常执行
pool-1-thread-1执行了第1个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-3执行了第4个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-2执行了第5个任务
pool-1-thread-4执行了第7个任务
九个任务时,执行完前八个任务后报异常处理
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.ArrayQueueDemo$$Lambda$1/1096979270@3b9a45b3 rejected from java.util.concurrent.ThreadPoolExecutor@7699a589[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 1]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.ArrayQueueDemo.main(ArrayQueueDemo.java:20)
2.new ThreadPoolExecutor.CallerRunsPolicy()
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//拒接策略:哪来的去哪(主线程来的,就回去让主线程执行)
new ThreadPoolExecutor.CallerRunsPolicy()
);
for(int i = 1;i<=9;i++){
//使用了线程池之后,使用线程池来创建线程
int temp = i;
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
});
}
}
核心思想:哪来的去哪(主线程来的,就回去让主线程执行)
pool-1-thread-1执行了第1个任务
pool-1-thread-4执行了第7个任务
main执行了第9个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-3执行了第5个任务
pool-1-thread-4执行了第4个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-5执行了第8个任务
3.new ThreadPoolExecutor.DiscardPolicy()
丢掉了第九个任务,不报异常
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//拒绝策略:丢掉任务,不抛出异常
new ThreadPoolExecutor.DiscardPolicy()
);
for(int i = 1;i<=9;i++){
//使用了线程池之后,使用线程池来创建线程
int temp = i;
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
});
}
}
pool-1-thread-1执行了第1个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-4执行了第7个任务
pool-1-thread-3执行了第4个任务
pool-1-thread-1执行了第3个任务
pool-1-thread-2执行了第5个任务
4、new ThreadPoolExecutor.DiscardOldestPolicy()
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//拒绝策略:阻塞队列满了,把最前面阻塞队列的任务丢掉,并执行当前任务,也不会抛出异常!
new ThreadPoolExecutor.DiscardOldestPolicy()
);
for(int i = 1;i<=9;i++){
//使用了线程池之后,使用线程池来创建线程
int temp = i;
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了第"+ temp+"个任务");
});
}
}
可以看出把第三个任务给挤掉了去执行了第九个任务
pool-1-thread-1执行了第1个任务
pool-1-thread-4执行了第7个任务
pool-1-thread-5执行了第8个任务
pool-1-thread-3执行了第6个任务
pool-1-thread-2执行了第2个任务
pool-1-thread-5执行了第9个任务
pool-1-thread-4执行了第5个任务
pool-1-thread-1执行了第4个任务
四、总结
线程池的设计思路为:线程池为一个创建管理线程的工厂,把执行任务分配给各个线程去执行,当线程数量不满足任务数量时。会暂时把任务防止阻塞队列中,等待空闲线程来执行。当阻塞队列放置满了,就会创建新线程去执行多出的任务。当任务足够多,以至于创建的线程达到最大线程数时,就会采用拒绝策略不接收任务。最后,当任务量小了,大部分线程执行完任务后,转变为待执行的线程时。会清理掉一些线程数量,以保证线程利用率和降低资源开销。