目录
1 )核心线程池的底层实现(ExecutorService类)
1.3)workQueue工作对列是指提交未执行的任务队列:
1.5)扩展RejectedExecutionHandler接口
5.1)submit()提交线程,如果抛出异常,会被线程池吃掉。实际上没被吃掉,只是返回到Future了
1 )核心线程池的底层实现(ExecutorService类)
1.1)
查看Executors工具类中newCachedThreadPool(),newSingleThreadExcecutor(),new FixedThreadPool()源码
1.2)threadPoolExecutor的构造方法:
public ThreadPoolExecutor(int corePoolSize, //指定线程池中核心线程的数量
int maximumPoolSize, //指定线程池中最大线程的数量
long keepAliveTime,//当线程池线程的数量超过corePoolSize时,多余的空闲线程的存活时长,既空闲线程在多长时长内销毁
TimeUnit unit,//是keepAliveTime时长单位
BlockingQueue<Runnable> workQueue,//任务队列,把任务提交到该任务任务队列中等待执行
ThreadFactory threadFactory,//线程工厂,用于创建线程
RejectedExecutionHandler handler) //拒绝策略,当任务太多来不及处理,如何拒绝
1.3)workQueue工作对列是指提交未执行的任务队列:
- 直接提交队列,由SynchronousQueue对象提交,实际上就是不缓存,直接交给线程执行,没有多余线程则创建,如果线程超过了最大线程数量,执行拒绝策略
- 有界任务队列,由ArrayBlockingQueue对象提交,先判断线程数是否小于核心线程数,在判断任务队列是否已满,最后判断线程数是否大于最大线程数
- 无界任务队列,由LinkedBlockingQueue对象实现。只要系统资源未耗尽,一直向队列缓存。当有新任务时,在系统线程数小于corePoolsize核心线程数则创建新的线程,否则进入阻塞对列
- 优先任务队列,由PriorityBlockingQueue实现的,是带有任务优先级的队列,是一个特殊的无界队列。在PriorityBlockingQueue队列中可以根据任务优先级顺序先后执行。
1.4)拒绝策略
ThreadPoolExecutor构造方法的最后一个参数指定了拒绝策略,当提交给线程池的任务量超过实际承能力(线程已经用完,等待队列也满了)时,如何处理
- AbortPolicy策略,会抛出异常(Executors工具类默认策略)
- CallerRunsPolicy策略,只要线程池没关闭,会在调用者线程中运行当前被丢弃的任务
- DiscardPolicy直接丢弃这个无法处理的任务
- DiscardOldestPolicy 将任务队列中最老的任务丢弃,尝试再次提交新任务
1.5)扩展RejectedExecutionHandler接口
//定义任务
Runnable r = new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(5);
System.out.println(Thread.currentThread().getId() + "..."+System.currentTimeMillis()+"开始睡眠"+num+"秒");
try {
TimeUnit.SECONDS.sleep(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//创建线程池,自定义拒绝策略
//LinkedBlockingDeque是无界队列,但10限止了其大小
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
//runnable就是请求的任务,threadPoolExecutor就是当前线程池
System.err.println(r+"is discarding...");
}
});
for (int i = 0; i < Integer.MAX_VALUE; i++) {
threadPoolExecutor.submit(r);
}
1.6)扩展ThreadFactory接口
例:使用ThreadFactory创建的接口都属于守护线程
//定义任务
Runnable r = new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(10);
System.out.println(Thread.currentThread().getId() + "..."+System.currentTimeMillis()+"开始睡眠"+num+"秒");
try {
TimeUnit.SECONDS.sleep(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread t = new Thread(runnable);
//设置创建的线程都是守护线程
t.setDaemon(true);
return t;
}
});
for (int i = 0; i < 5; i++) {
executorService.execute(r);
}
2)监控线程池
对线程池运行状态进行监控
- int getActiveCount() 获得线程池中当前活动线程的数量(活动指正在执行)
- long getCompletedTaskCount() 返回线程池完成任务的数量
- int getCorePoolSize() 线程池中核心线程的数量(等于corePoolSize参数)
- int getLargestPoolSize() 返回线程池曾经达到的线程的最大数
- int getMaximumPoolSize() 返回线程池的最大容量(等于maximumPoolSize参数)
- int getPoolSize() 当前线程池的大小
- BlockingQueue<Runnable> getQueue() 返回阻塞队列
- long getTaskCount() 返回线程池收到的任务总数(有效的任务数,丢弃的任务不算)
//定义任务
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "..."+System.currentTimeMillis()+"开始睡眠"+10+"秒");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 10, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 30; i++) {
poolExecutor.execute(r);
System.out.println(" 当前线程池核心线程数量 :" + poolExecutor.getCorePoolSize() + ", 最大线程数:" + poolExecutor.getMaximumPoolSize() + ",当前线程池大小 :" + poolExecutor.getPoolSize() + ",活动线程数量 :" + poolExecutor.getActiveCount()+ ",收到任务数量:" + poolExecutor.getTaskCount() + ",完成任务数 : " + poolExecutor.getCompletedTaskCount() + ", 等待任务数:" + poolExecutor.getQueue().size()) ;
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
===
11...1613299015374开始睡眠10秒
当前线程池核心线程数量 :5, 最大线程数:10,当前线程池大小 :1,活动线程数量 :1,收到任务数量:1,完成任务数 : 0, 等待任务数:0
解释:
- 当前线程池核心线程数量: 一直为5 等于corePoolSize参数
- 最大线程数:一直为10 等于maximumPoolSize参数
- 当前线程池大小 先是从1到5,然后等待任务数从1到5后,线程池大小再从5到10。与 ArrayBlockingQueue等待对列有关
- 活动线程数量,先是从1到5,然后等待任务数从1到5后,活动线程再从5-10,与ArrayBlockingQueue等待对列有关
- 收到任务数量,从0-15,然后开始丢弃任务,然后完成一个才能接收一个 DiscardPolicy设置
- 等待任务数,先是0,当线程池大小到达核心线程数量后,从0到5
3)扩展线程池--监控每个任务的开始和结束
我们要监控任务只要重写上面两个方法即可。
//定义任务类
private static class MyTask implements Runnable{
String name;
public MyTask(String name){this.name = name;}
@Override
public void run() {
System.out.println(name+"任务正在被线程 "+Thread.currentThread().getId()+"执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//定义扩展线程池,可以定义线程池类继承ThreadPoolExecutor,在子类中重写beforeExecute()/afterExecute()方法
//也可以直接使用ThreadPoolExecutor的内部类
ExecutorService executorService = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>()){
@Override
protected void beforeExecute(Thread thread, Runnable runnable) {
System.out.println(thread.getId()+"线程准备执行任务:"+((MyTask)runnable).name);
}
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
System.out.println(((MyTask)runnable).name+"任务执行完毕");
}
@Override
protected void terminated() {
System.out.println("线程池退出");
}
};
for (int i = 0; i < 5; i++) {
MyTask task = new MyTask("task-"+i);
executorService.execute(task);
// executorService.submit(task); 使用submit提交有强转错误
}
executorService.shutdown(); //关闭线程池仅仅是说线程池不再接收新的任务,线程也已接收的任务正常执行完毕
}
====
12线程准备执行任务:task-1
11线程准备执行任务:task-0
task-1任务正在被线程 12执行
task-0任务正在被线程 11执行
task-1任务执行完毕
task-0任务执行完毕
线程池退出
3)优化线程池大小
线程池极大极小都会影响性能,一般来说,线程池大小需要考虑cpu数量,内存大小等因素。
线程池大小 = cpu的数量×目标cpu的使用率×(1+等待时间/计算时间)
4)线程池死锁
与多线程死锁有区别,线程池死锁说的以因为线程池的原因导致了程序的死锁。
任务A在执行的过程中又向线程池提交了任务B,并且任务A依赖任务B的执行结果。但因为线程池负载的原因,任务B未执行,导致死锁。
5)线程中的异常处理
使用submit()提交线程,出现异常也不会显示
5.1)submit()提交线程,如果抛出异常,会被线程池吃掉。实际上没被吃掉,只是返回到Future了
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
Future<?> s = threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
}
});
try {
Thread.sleep(2000);
System.out.println("提交的异常:"+s.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
5.2)可以将submit()修改为execute
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
}
});
5.3)重写submit()方法,使其抛出异常
实际上也就是重写了run方法
private static class TraceThreadPoollExecutor extends ThreadPoolExecutor{
public TraceThreadPoollExecutor(int i, int i1, long l, TimeUnit timeUnit, BlockingQueue<Runnable> blockingQueue) {
super(i, i1, l, timeUnit, blockingQueue);
}
//定义方法,对执行的任务进行包装
public Runnable wrap(Runnable task, Exception e){
return new Runnable() {
@Override
public void run() {
try {
task.run();
}catch (Exception e){
e.printStackTrace();
throw e;
}
}
};
}
@Override
public Future<?> submit(Runnable runnable) {
return super.submit(wrap(runnable,new Exception("客户跟踪异常")));
}
}
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new TraceThreadPoollExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
}
});
}
6)ForkJoinPool线程池
ForkJoinPool采用分而治之原理,是大数据处理方法。把一个大任务调用fork()方法分解为若干小的任务,把小任务的处理结果进行join()合并为大任务的结果。
ForkJoinPool线程池最常用的方法是:<T> ForkJoinTask<T> submit(ForkJoinTask<T> task>向线程池提交一个ForkJoinTask任务,ForkJoinTask任务支持fork()分解与join()等待的任务。ForkJoinTask有两个重要的子类,RecursiveAction和RecursiveTask,区别在于RecursiveAction任务没有返回值。
在ForkJoinPool线程池中,线程的创建数是随程序的运行决定的,而不是人为指定的。
fork()就像开启一个线程,去执行compute()。而join()就是去获取 compute()执行的结果(没执行完等待)。
且是通过ForkJoinPool去调用继承RecursiveTask的类
public class Test09 {
//计算数列的和,需要返回结果,可以定义任务继承RecursiveTask
private static class CountTask extends RecursiveTask<Long>{
private static final int THRESHOLD = 10000; //定义数据规模的阈值,允许计算10000个数内的和,超过该阈值的数列就需要分解
private static final int TASKNUM = 100; //定义每次把大任务分解为100个小任务
private long start =0;//开始
private long end = 0;//结束
public CountTask(long start, long end) {
this.start = start;
this.end = end;
}
//重写RecursiveTask类的compute()方法,计算数列的结果
@Override
protected Long compute() {
long sum = 0;
if(end -start< THRESHOLD){
for (long i = start;i <= end; i++) {
sum += i;
}
}else {
//约定每次分解成100个小任务,计算每个任务的计算量
long step=(start+end)/TASKNUM;
ArrayList<CountTask> subTaskList=new ArrayList<>();
long pos = start;
for (int i = 0; i < TASKNUM; i++) {
long lastOne = pos+ step;
if(lastOne > end) lastOne = end;
//创建子任务
CountTask task = new CountTask(pos,lastOne);
//把任务添加到集合中
subTaskList.add(task);
//调用for()提交子任务
task.fork();
//调整下个任务的起始位置
pos += step+1;
}
//等待所有的子任务结束后,合并计算结果。
for (CountTask task : subTaskList) {
sum += task.join();
}
}
return sum;
}
}
public static void main(String[] args) {
//创建ForkJoinPool线程池
ForkJoinPool forkJoinPool = new ForkJoinPool();
//创建一个大任务
CountTask task = new CountTask(0L,200000L);
//把大任务提交给线程池
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
try {
Long res = submit.get();
System.out.println("计算结果为:"+res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
long r=0;
for (int i = 0; i <= 200000L; i++) {
r+=i;
}
System.out.println(r);
}
}