线程池
线程池的概述
线程池:装线程对象的容器
线程池的应用场景:
如果在一个应用程序中需要多次使用一些线程,不使用线程池的话,咱就需要多次创建并销毁这些线程,而创建又销毁又创建...线程的过程会不断消耗内存
为此产生了线程池这个概念,把这些线程放到线程池中,当有线程任务来的时候,这些线程就出线程池去完成这些线程任务,当线程任务完成了之后,这些线程就再回到线程池中等待下一次的线程任务的到来,线程池规避了创建又销毁又创建线程这些过程,节省了内存损耗,提高了效率
创建线程池对象的两种方法
面向对象的方式创建线程池对象
概述
面向对象的方式创建线程池对象:
1.Executors工具类调用静态方法获取线程池对象
2.使用ExecutorService对象调用提交方法并传入线程任务
Executors.newCachedThreadPool():
不指定最大线程数的创建线程池的方法
--->但是该最大线程数不能超过int类型的最大值
Executors.newFixedThreadPool(int num):
指定最大线程数的创建线程池的方法
--->如果线程任务小于该线程池的最大线程数,则只会创建与线程任务数量大小匹配的线程池,是个限制了最大容量的动态的过程
例如:现在指定了线程池的最大线程数为100,但是只有十个线程任务需要完成,则不会创建最大线程数为100的线程池,而是会创建最大线程数为10的线程池
代码实现
package com.tan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService es1 = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
es1.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了");
}
});
}
es1.shutdown();
ExecutorService es2 = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
es2.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了");
}
});
}
es2.shutdown();
}
}
面向过程的方式创建线程池对象
概述
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
用给定的初始参数创建新的 ThreadPoolExecutor。
注意:
1.这七个参数中所有的引用数据类型都不能为null
-->TimeUnit unit不能为null
-->BlockingQueue<Runnable> workQueue不能为null
-->ThreadFactory threadFactory不能为null
-->RejectedExecutionHandler handler不能为null
2.核心线程数不能比最大线程池中线程个数大 corePoolSize <= maximumPoolSize
3.long keepAliveTime不能小于0
方便记忆:
场景描述:假如说我们现在需要管理一家餐厅,且规定一个服务员只能服务一个顾客进行点餐、上菜、结账等,正式员工是固定的,
但是旅游旺季的时候,餐厅生意火爆,原有的正式员工忙不过来,那么我们就需要去人力市场雇用临时员工去增加人手。而且顾客
过多的时候,餐厅内的座位是不是就坐满了,人们就需要在餐厅外面排队,但是每天餐厅准备的食物原材料是有限的,只够一定的
顾客进餐,那么我们就需要和超过准备的事物原材料的人数的顾客进行沟通,让其改日再来或者去隔壁餐厅就餐
这里的:
-->正式员工就可以理解为:int corePoolSize,
-->员工总人数(正式员工+临时员工):int maximumPoolSize,
-->临时员工雇用的时长:long keepAliveTime,
-->餐厅外面排队的顾客:BlockingQueue<Runnable> workQueue,
-->人力市场:ThreadFactory threadFactory,
-->和超过准备的事物原材料的人数的顾客进行沟通的说辞:RejectedExecutionHandler handler)
一共能在本餐厅就餐的顾客数为:员工总人数(正式员工+临时员工) + 店外排队的人(可以就餐的顾客)
-->int maximumPoolSize + BlockingQueue<Runnable> workQueue
四种等待拒绝策略
类ThreadPoolExecutor中四种等待拒绝策略:
1)static class ThreadPoolExecutor.AbortPolicy
用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException.
--->推荐使用(及时告知我们,还有线程任务没有完成)
--->拒绝所有不在线程池及阻塞队列中的线程任务,并报错
2)static class ThreadPoolExecutor.CallerRunsPolicy
用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。
--->不推荐使用(都不提醒咱部分线程任务没有完成,可能会影响后续代码)
--->拒绝所有不在线程池及阻塞队列中的线程任务,不报错
3)static class ThreadPoolExecutor.DiscardOldestPolicy
用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。
--->拒绝所有不在线程池及阻塞队列(除了等待最久的线程任务Oldest)中的线程任务,
并“随机”选取一个在线程池及阻塞队列中的线程任务移除出去,将等待最久的线程任务Oldest添加进来
4)static class ThreadPoolExecutor.DiscardPolicy
用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
--->拒绝所有不在线程池及阻塞队列中的线程任务,并呼叫其他的线程来处理这些线程任务
代码实现
package com.tan.create_threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*
面向过程的方式创建线程池对象
ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程池中线程个数
long keepAliveTime,//临时线程存活时间数值
TimeUnit unit,//临时线程存活时间单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂 -->线程池获取线程对象的地方
RejectedExecutionHandler handler)//等待拒绝策略
用给定的初始参数创建新的 ThreadPoolExecutor。
*/
public class CreateThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,//核心线程个数
3,//总线程池的大小
20,//时间数值
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(2),//阻塞队列中线程的个数
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.AbortPolicy());//拒绝所有不在线程池及阻塞队列中的线程任务,并报错
for (int i = 1; i < 7; i++) {
final int count = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":顾客 " + count);
}
});
}
threadPool.shutdown();
}
}
运行结果
运行结果
1)AbortPolicy():拒绝所有不在线程池及阻塞队列中的线程任务,并报RejectedExecutionException异常
pool-1-thread-1:顾客 1
pool-1-thread-3:顾客 5
pool-1-thread-2:顾客 2
pool-1-thread-1:顾客 3
pool-1-thread-3:顾客 4
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 2]
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 java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.tan.b_create_threadpool.CreateThreadPool.main(CreateThreadPool.java:64)
2)DiscardPolicy():拒绝所有不在线程池及阻塞队列中的线程任务,不报错
pool-1-thread-1:顾客 1
pool-1-thread-3:顾客 5
pool-1-thread-2:顾客 2
pool-1-thread-1:顾客 3
pool-1-thread-2:顾客 4
3)DiscardOldestPolicy():拒绝所有不在线程池及阻塞队列(除了等待最久的线程任务Oldest)中的线程任务,
并“随机”选取一个在线程池及阻塞队列中的线程任务移除出去,将等待最久的线程任务Oldest添加进来
pool-1-thread-1:顾客 1
pool-1-thread-3:顾客 5
pool-1-thread-1:顾客 4
pool-1-thread-2:顾客 2
pool-1-thread-3:顾客 6
4)CallerRunsPolicy():拒绝所有不在线程池及阻塞队列中的线程任务,并呼叫其他的线程来处理这些线程任务
main:顾客 6
pool-1-thread-3:顾客 5
pool-1-thread-2:顾客 2
pool-1-thread-1:顾客 1
pool-1-thread-2:顾客 4
pool-1-thread-3:顾客 3