什么是线程池?
线程池:顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程,而是返回池中从而减少创建和销毁对象对CPU和内存的开销.
例如:线程池的概念类似人才资源中心,原本每次要运行一个线程(公司找一个员工)都必须去创建(培养)一个员工才行,有了线程池之后就在线程池中准备了多个创建好的线程(培训好的员工)等待被调用
线程池的主要作用?
1、不同请求之间重复利用线程,无需频繁的创建和销毁线程,降低系统开销;
2、控制线程数量上限,避免创建过多的线程耗尽进程内存空间,
3、能更好的控制线程的开启与回收,并且能定时执行任务。
线程池的核心原理?
1、 线程池由两个核心数据结构组成:
1)线程集合(workers):存放执行任务的线程,是一个HashSet;
2)任务等待队列(workQueue):存放等待线程池调度执行的任务,是一个阻塞式队列BlockingQueue;
2、任务由execute方法提交到线程池中调度,在提交任务时会有下面几种场景:
1)线程池中线程数量小于corePoolSize,此时任务不会进等待队列,线程池直接创建一个线程Worker执行提交的任务;
2)线程池中线程数量不小于corePoolSize并且等待队列未满,任务直接添加到等待队列,等待线程池调度执行;
3)线程池中线程数量不小于corePoolSize但是等待队列已满且线程数量小于maximumPoolSize,线程池会进行扩容新创建一 个线程Worker执行提交的任务,新创建的Worker会被添加到线程集合workers中;
4)等待队列已满并且线程数量已达到maximumPoolSize,这种情况下线程池无法继续执行任务会拒绝任务,执行一个指定的 拒接策略。
5)线程池已关闭,拒绝任务,执行一个指定的拒接策略。
线程创建之后,会不停从等待队列workQueue中拉取任务,workQueue是一个线程安全的阻塞队列,所以不存在线程安全问 题,拉取到任务之后,执行任务逻辑。拉取任务时有两种情况:
1)线程池设置了keepAliveTime参数,并且此时线程池中的线程数量超过核心数量corePoolSize,从队列中拉取任务时会设 置 keepAliveTime为超时时间,超过这个时间之后,该线程不再等待任务,直接跑完run方法体,线程被回收;
2)否则线程会无限等待任务队列直到有任务到来。
关闭线程池
关闭线程池时有两个关键步骤:
1)修改线程池状态到SHUTDOWN,这时新提交到线程池的任务都会被直接拒绝;
2)中断线程池中的所有线程,中断任务执行回收线程集合中所有线程。
线程池的五种状态:
running(运行状态):线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
shutdown(关闭状态):拒绝新任务但是处理阻塞队列里的任务
stop(停止状态):拒绝新任务并且抛弃阻塞队列里的任务同时会中断正在处理的任务
tidying ():所有任务都执行完(包含阻塞队列里面任务)当前线程池活动线程为0,将要调用terminated方法
terminated(结束):终止状态。terminated方法调用完成以后的状态
线程池的核心类:ThreadPoolExecutor
构造函数参数
1、corePoolSize: 核心线程数大小,当线程数 < corePoolSize ,会创建线程执行 runnable
2、maximumPoolSize: 最大线程数, 当线程数 >= corePoolSize的时候,会把 runnable 放入 workQueue中
3、keepAliveTime: 空闲存活时间, 当线程数大于corePoolSize的空闲线程能保持的最大时间。
4、unit: 空闲存活时间单位
5、workQueue 保存任务的阻塞队列
6、threadFactory 创建线程的工厂
7、handler 拒绝策略
拒绝策略:
当线程集合和等待队列都满时线程无法调度任务,这时线程池会执行一个默认的或使用者指定的拒绝策略。
JDK内置的拒绝策略主要有下面几种:
1)调用线程执行(CallerRunsPolicy),任务被线程池拒绝后,任务会被调用线程执行;
2)终止执行(AbortPolicy),任务被拒绝时,抛出RejectedExecutionException异常报错
3)丢弃任务(DiscardPolicy),任务被直接丢弃,不会抛异常报错;
4)丢失老任务(DiscardOldestPolicy),把等待队列中最老的任务删除,删除后重新提交当前任务。
除了这些内置的拒绝策略,使用者还可以实现RejectedExecutionHandler接口自定义拒绝策略
JDK7提供了7个阻塞队列。(也属于并发容器)
1、ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
2、LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
3、PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
4、DelayQueue:一个使用优先级队列实现的无界阻塞队列。
5、SynchronousQueue:一个不存储元素的阻塞队列。
6、LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
7、LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
总结
①当工作线程小于核心线程时,会创建核心线程。
②如果线程池中的线程数量大于等于corePoolSize,但队列workQueue未满,则将新添加的任务放到workQueue中,按照FIFO的原则依次等待执行(线程池中有线程空闲出来后依次将队列中的任务交付给空闲的线程执行);
③当线程池处于非RUNNING状态时,会尝试将刚刚加入到队列的任务移除掉,如果移除失败,并且工作线程会0情况下,就会尝试创建一个非任务线程,来消费队列。
④如果线程池中的线程数量大于等于corePoolSize,且队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务;
⑤如果线程池中的线程数量等于了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
Java线程池ThreadPoolExecutor中execute()方法原理: