线程池常见的基础知识
使用线程池有哪些优势?
- 线程和任务分离,提升线程重用性
- 控制线程并发数量,降低服务器压力,统一管理所有的线程;
- 提升系统的响应速度,假如创建线程的时间为T1,执行任务的用的时间为T2,销毁线程用的时间是T3,那么使用线程池就免去了T1和T3的时间
线程池构造方法中的7个参数
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//最大空闲时间
TimeUtil unit,//时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略){...}
线程池原理
提交一个任务到线程池中,如果没有线程处理该任务,就会开启一个线程来执行提交的任务,再提交一个任务,再开启一个线程来执行.....达到核心线程数后接下来提交过来的任务先放入阻塞队列里面,队列没满的情况下,任务会由核心线程执行。
阻塞队列中任务放满了之后,10个核心线程处理不过来了,队列中都放不下去了,这个时候才会开启新的线程处理提交过来的任务。(相当于有两个地方会去创建线程)
线程池执行任务的具体流程是怎样的?
ThreadPoolExecutor中提供两种执行任务的方法
1. void executor(Runnable command)
2. Future<?> submit(Runnable task)
实际上submit中最终还是调用executor()方法,只不过会返回一个Future对象,用来获取任务执行结果。
public Future<?> submit(Runnable task){
if(task == null) throw new NullPointException();
RunnableFuture<Void> ftask = newTaskFor(task,null);
executor(ftask);
return ftask;
}
executor(Runnable command)执行的时候会分三步走:
核心线程数和最大任务数到底应该怎么设置呢?
这里是要区分Cpu密集型任务和IO密集型任务。
- cpu密集型
涉及到cpu运算比较多的情况,比如找到1-10000000中的素数,
- IO密集型
比如频繁的接口调用,网络传输等(本地磁盘IO也属于)
- 混合密集型
顾名思义就是两个都比较频繁
一旦线程返回空,那么这个线程也就消失掉了。
if(rs>= SHUTDOWN &&(rs >= STOP || workQueue.isEmpty())){
decrementWorkerCount();
return null;
}
中断线程池里的所有线程前提是调用这两个方法:executor.shutdown()和executor.shutdownNow()这两个方法。通过其他方式中断线程池中的线程是中断不了的。中断一个线程会去判断线程的状态是否是shutdown,仅仅是人为的干预中断。里面的for循环其实是还在运行,线程没有真正的中断。
executor.execute(new Runnable(){
@Override
public void run() {
System.out.println("...");
throw new NullPointerException();
}
});
executor.shutdown();
executor.shutdownNow();