启动一个新线程涉及与操作系统交互,成本比较高。线程池在程序启动时创建大量空闲线程,程序将线程对象传给线程池,线程就会启动一个线程来执行它的run()或call()方法,方法执行结束后,线程并不会死亡,而是再次返回线程池中称为空闲状态,等待下一次执行。使用线程池可以避免频繁创建和销毁线程,可以使创建的线程得到复用,同时使用线程池可以有效控制系统中并发线程的数量。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(参数);
public static void main(String[] args) {
MyTestThreadPool myTestThreadPool = new MyTestThreadPool(3,3,0L,TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(10));
for (int i = 0; i < 100; i++) {
Runnable target = () -> {
for (int j = 0; j < 10; j++) {
System.out.println("j = " + j);
}
};
myTestThreadPool.submit(target);
}
}
线程池全参构造源码:
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize
指定线程池中核心线程数
maximumPoolSize
指定线程池中最大线程数量:核心线程数量+非核心线程数量
keepAliveTime
当线程池中线程数量超过corePoolSize
时,多余线程存活的时间(非核心线程存活时间),即非核心线程在多长时间后被销毁
unit
keepAliveTime
的时间单位
workQueue
任务队列,被提交到那时尚未被执行的任务
threadFactory
线程工厂,用于创建线程
handler
拒绝策略。当任务太多来不及处理时,如何拒绝任务。
SynchronousQueue
SynchronousQueue没有容量,每一个put操作都需要等待一个对应的take操作。如果使用SynchronousQueue,则提交的任务不会被真实地保存,而是将新的任务提交给线程执行。
ArrayBlockingQueue
数组实现的有界阻塞队列,ArrayBlockingQueue的构造函数必须带参数,指定该队列的最大容量。此队列按照先进先出的顺序对元素进行排序。可以通过构造函数设定队列是否公平new ArrayBlockingQueue(int capacity, boolean fair)
。默认情况下为非公平的访问队列。
LinkedBlockingQueue
LinkedBlockingQueue是基于链表实现。LinkedBlockingQueue中对生产者和消费者分别采用了独立的锁来控制数据同步,这也意味着在高并发情况下,生产者和消费者可以并行操作队列中的数据,提高整个队列的并发性能。
PriorityBlockingQueue
优先任务队列,带有执行优先级,可以控制任务执行的先后顺序。可以自定义是compareTo方法来实现排序,也可以指定构造参数Comparator来对数据进行排序。
DelayQueue
是一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue实现,队列中的元素必须实现Delayed
接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延时期满时才能从队列中提取元素。
当线程池中的任务数量超过负载时就会使用到拒绝策略。JDK中内置了四种拒绝策略:
AbortPolicy
:该策略直接抛出异常,阻止线程继续工作;CallerRunsPolicy
:只要线程池未关闭,该策略直接在调用者线程中运行当前任务。这种方式可能造成任务提交线程的性能急剧下降;DiscardOldestPolicy
:该策略会丢弃最老的请求,也就是即将执行的一个任务,并尝试再次提交;DiscardPolicy
:该策略默认丢弃无法处理的任务。自行扩展RejectedExecutionHandler
接口。
简单描述: 核心线程(不满足时下一步,否则执行任务) -> 等待队列(不满足时下一步,否则执行任务) -> 非核心线程(不满足时下一步,否则执行任务) -> 拒绝策略。
具体步骤:
corePoolSize
,则马上创建线程执行这个任务;corePoolSize
,那么将这个任务放入等待队列;maximumPoolSize
,则创建非核心线程处理该任务;maximumPoolSize
,则会抛出RejectedExecutionHandler
(线程池设置的拒绝策略)。maximumPoolSize
,这个线程就会被干掉,线程池线程数量收缩到核心线程数大小。java.util.concurrent.Executors
工具类中提供了具有特定功能的线程池。
该方法创建一个可重用、有固定线程数量的线程池。该池中的线程数量始终不变。当有一个新任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被放入等待队列。
源码分析:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
该线程池核心线程数与最大线程数相等,使用LinkedBlockingQueue
作为等待队列,该队列的默认大小为Integer.MAX_VALUE
,当任务提交非常频繁时,可能导致线程大量堆积。
该方法创建一个只有单线程的线程池,若提交任务时线程被詹总,则任务进入等待队列,待线程空闲,按照先进先出的顺序执行队列中的任务。
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
该线程池核心线程池与最大线程池数均为1,使用LinkedBlockingQueue
作为等待队列,该队列的默认大小为Integer.MAX_VALUE
,可能导致线程大量堆积。
该方法创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程会被缓存在线程池中。线程池中线程数量不确定,但若有空闲线程可以复用,会优先使用可复用的线程。若所有线程均在工作,则会创建新的线程处理任务。所有线程在当前线程执行完毕后,将返回线程池进行复用。
源码分析:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
该线程池核心线程数为0,最大线程数为Integer.MAX_VALUE
,使用了SynchronousQueue
等待队列,该队列不存储任务,放入一个任务必须等待一个线程来处理任务。空闲线程会在60秒后被回收。使用该线程池,若同时有大量任务提交,而任务执行并不快,那么系统会创建大量线程来处理任务,这样做可能会很快耗尽系统资源。
该方法创建具有指定线程数量的线程池,它可以在指定延迟后执行线程任务或周期性执行某个任务。该方法返回一个ScheduledExecutorService
对象。
源码分析:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
该方法返回ScheduledExecutorService
对象,线程池的大小为1。
源码分析:
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
该方法床架持有足够线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争。
源码分析:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
1、自定义线程池时重写protected void afterExecute(Runnable r, Throwable t)
方法,对异常进行处理;
2、定义自己的异常处理器,通过Thread的setUncaughtExceptionHandler(new MyExHandler());
方法,设置异常处理器。
class MyExHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程" + t + "出现了异常:" + e);
}
}