使用Executors
可以查看所有的JDK封装好的线程池
public static void main(String[] args) {
// 1、定长线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
// 2、可缓存线程池
ExecutorService catchThreadPool = Executors.newCachedThreadPool();
// 3、单线程线程池
ExecutorService singleThread = Executors.newSingleThreadExecutor();
// 4、定时线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// 5、单线程定时线程池
ScheduledExecutorService singleThreadScheduled = Executors.newSingleThreadScheduledExecutor();
//6、 任务窃取线程池
ExecutorService workStealingPool = Executors.newWorkStealingPool();
}
1、定长线程池源代码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
(1)核心线程数量等于最大线程数量,一旦被创建指定的线程数量,就不会被垃圾回收,除非显式地调用了线程池的shutdown或shutdownNow方法来关闭线程池。
(2)默认为无界任务队列
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
2、可缓存线程池,源代码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
(1)没有核心线程,不限制线程数量,空闲线程超过60秒不工作会被回收
(2)直接提交队列,来了任务就创建线程,创建和销毁线程会浪费大量时间
3、单线程线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
(1)只有一个核心线程,是一个无界队列
(2)好处是避免了并发问题
4、定时线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
(1)不限制线程数量,一旦线程空闲下来会立马被销毁
(2)DelayedWorkQueue,阻塞队列
5、单线程定时线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
6、任务窃取线程池
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
线程池的核心组成部分及其运行机制
- (1)corePoolSize:指定线程池中,核心线程的数量
- (2)maximumPoolSize:指定线程池中,最大线程数量
- (3)keppAliveTime:当线程池线程的数量超过 corePoolSize 时,多余的空闲线程的存活时长,即空闲线程在多长时间内销毁
- (4)unit:是 keppAliveTime 时长单位
- (5)workQueue:任务队列,把任务提交到该任务队列中等待执行
- (6)ThreadFactory:线程工厂,用于创建线程
- (7)handler:拒绝策略,当任务太多,来不及处理时,如何拒绝
workQueue工作队列是指提交未执行的任务队列,它是BlockingQueue接口的对象,仅用于存储Runnable任务。根据队列功能分类,在ThreadPoolExecutor构造方法中可以使用以下几种阻塞队列:
(1)直接提交队列:由 SynchronousQueue 对象提供,该队列没有容量,提交给线程池的任务不会被真实的保存,总是将新的任务提交给线程执行,如果没有空闲线程,则尝试创建新线程,如果线程数量已经达到 maximumPoolSize 规定的最大值则执行拒绝策略。
该线程池在极端情况下,每次提交新的任务都会创建先拿到线程执行,适合用来执行大量耗时短并且提交频繁的任务。
(2)有界任务队列:由 ArrayBlockingQueue 实现,在创建 ArrayBlockingQueue 对象时,可以知道一个容量,当有任务需要执行时,如果线程池中线程数小于 corePoolSize 核心线程数,则创建新的线程;如果大于核心线程数则加入等待队列,如果队列已满则无法加入,在线程数小于 maximumPoolSize 指定的最大线程数前提下会创建新的线程来执行,如果线程数大于 maximumPoolSize 则执行拒绝策略。
(3)无界任务队列:由 LinkedBlockingQueue 对象实现,与有界队列相比,除非系统资源耗尽,否则无界队列不存在任务失败的情况,当有新的任务时,在系统线程数小于 corePoolSize 核心线程数则创建新的线程来执行任务;当线程池中线程数量大于 corePoolSize 核心线程数则把任务加入阻塞队列。
(4)优先任务队列:由 PriorityBlockingQueue 实现,是带有任务优先级的队列,是一个特殊的无界队列,不管是 ArrayBlockingQueue 队列,还是 LinkedBlockingQueue 队列都是按照先进先出算法处理任务的,在 PriorityBlockingQueue 队列中可以根据任务优先级顺序先后执行。
ThreadFactory
ThreadFactory 是一个接口,只有一个用来创建线程的方法:
Thread newThread(Runnable r);
当线程池中需要创建线程时就会调用该方法。
通过 new ThreadFactory() 来自定义线程工厂。
拒绝策略
ThreadPoolExecutor构造方法的最后一个参数指定了拒绝策略,当提交给线程池的任务量超过实际承载能力时,如何处理?
即线程池汇中的线程已经用完了,等待队列也满了,无法为新提交的任务服务,可以通过拒绝策略来处理这个问题。JDK提供了四种拒绝策略:
(1)AbortPolicy:抛出异常
(2)CallerRunsPolicy:只要线程池没有关闭,会在调用者线程中运行当前被丢弃的任务
(3)DiscardOldestPolicy:将任务队列中最老的任务丢弃,尝试再次提交任务
(4)DiscardPolicy:直接丢失这个无法处理的任务
Executors 工具类提供的静态方法返回的线程池默认的拒绝策略是 AbortPolicy,抛出异常。
通过 new RejectedExecutionHandler() 来自定义拒绝策略。
自定义线程池
package threadpool;
import java.util.concurrent.*;
public class CustomizeThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 100, 30, TimeUnit.SECONDS, new ArrayBlockingQueue(101));
for(int i=0; i<101; i++){
final int index = i;
pool.execute(new Thread(new Runnable() {
public void run() {
System.out.println(index + "被执行线程名:" + Thread.currentThread().getName());
}
}));
}
}
}