一、线程池好处:
二、线程池创建:
1、Executor接口:
public interface Executor {
void execute(Runnable command);
}将任务提交和执行解耦,参数传入Runnable(任务),通过execute方法进行任务执行。
2、ThreadPoolExcutor:
线程池核心实现类:
public class ThreadPoolExecutor extends AbstractExecutorService {public abstract class AbstractExecutorService implements ExecutorService {public interface ExecutorService extends Executor {- ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
- 抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
- 然后ThreadPoolExecutor继承了类AbstractExecutorService;
3、ThreadPoolExcutor 构造和参数
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}- corePoolSize:核心线程数。默认线程池为空,任务提交会创建线程,当前线程数少于corePoolSize会创建新线程,大于或等于corePoolSize则不创建。
- maximumPoolSize:最大线程数。如果任务队列满了,线程数小于maximumPoolSize,线程池会创建新线程处理任务。
- keepAliveTime:非核心线程闲置的超时时间。非核心线程,如果没任务可执行,超过时间后会被回收。设置allowCoreThreadTimeOut属性为true,keepAliveTime也作用于核心线程。
- unit:keepAliveTime时间参数,天、小时、分钟、秒、毫秒等。
- workQueue:任务队列。当前线程数大于corePoolSize,新任务会被添加到任务队列,队列为BlockingQueue(阻塞队列)。
- ArrayBlockingQueue:数组组成的有界阻塞队列
- LinkedBlockingQueue:链表组成的有界阻塞队列
- PriorityBlockingQueue:优先级排序的无界阻塞队列
- DelayQueue:用优先级队列实现的无界阻塞队列
- SynchronousQueue:不存储元素的阻塞队列
- LinkedTransferQueue:链表组成的无界阻塞队列
- LinkedBlockingDeque:链表组成的双向阻塞队列
- threadFactory:线程工厂。可用线程工厂给创建的线程设置名字。
- handler:饱和策略。线程和队列都满了采用的策略。默认abordPolicy,无法处理新任务,并抛出RejectedExecutionException异常。还有其他三种策略:
- CallerRunsPolicy:由调用者所在线程处理该任务
- DiscardPolicy:丢弃该任务,不抛异常
- DiacardOldestPolicy:丢弃队列最近的任务,重新尝试执行该任务
4、线程池处理流程:
三、线程池种类:
Executors类提供了创建常见线程的方法,主要有四种,FixedThreadPool、CachedThreadPool、SingleThreadExecutor和ScheduledThreadPool。
1、FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}可以指定核心线程数和最大线程数。也就是说只有核心线程数,并且线程不会被销毁。任务队列采用的是LinkedBlockingQueue无界阻塞队列。
流程图:
(1)执行execute提交任务,如线程数小于核心线程数,则创建核心线程并执行任务。否则加入阻塞队列。
(2)核心线程如果执行完当前任务,会在队列中取任务并执行。
2、CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,最大线程数为Integer.MAX_VALUE,空闲线程等待时长位60秒。使用的阻塞队列为SynchronousQueue,该队列不存储元素,每个插入操作必须等另一个线程的移除操作,每个移除操作必须等待另一个线程的插入操作。
适用于少量的几个任务的场景。
流程图:
(1)执行execute提交任务,插入SynchronousQueue队列,同时判断是否有空闲线程,如有则将任务交给空闲线程去取任务处理;如果没有,则创建非核心线程去处理任务(从队列移除)。如果超过60秒无任务,空闲线程会被回收。
适用于大量任务,并且耗时不多的场景。
3、SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}只有一个核心线程的线程池。采用的阻塞队列为LinkedBlockingQueue。
流程图:
(1)执行execute提交任务,如无核心线程,创建一个核心线程执行任务;如有线程在运行,则将任务加入到队列,等核心线程处理完当前任务,再从队列中取任务处理。
适用于多任务有序进行的场景。
4、ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}固定的核心线程数,最大线程数为Integer.MAX_VALUE,队列为DelayedWorkQueue可延时获取数据的无界阻塞队列,所以不会创建非核心线程。
返回了ScheduledThreadPoolExecutor对象,主要用来执行延时任务或定期任务。
流程图:
(1)执行scheduleAtFixedRate或scheduleWithFxiedDelay方法,执行周期性任务或者延时任务时,将任务添加到队列中。并且检查是否达到了核心线程数,没达到就创建核心线程。
(2)DelayedWorkQueue将任务进行排序,核心线程会在队列中取到任务的包装类ScheduledFutureTask,然后去执行任务。
适用于执行周期任务或延时任务。