为什么要使用线程池?
- 为了减少资源的开销,提高系统性能
- 先创建好线程,如果需要使用从池中取,提高利用率
- 控制线程数量,防止程序崩溃
Java 中创建线程池的两种方式?
- 使用ThreadPoolExecutor类
- 使用Executors类
其实这两种方式在本质上是一种方式,都是通过ThreadPoolExecutor类的方式
一、ThreadPoolExecutor的方式
1、使用方法
查看JDK的源码,ThreadPoolExecutor类提供了以下构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
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:线程池空闲时线程的存活时间
- unit:线程池存活时间单位
- workQueue:存放任务队列,使用的是阻塞队列
- rrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
- 方法处理方式 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time,unit)
检查方法 element() peek() 不可用 不可用
- threadFactory:线程池创建线程的工厂
- handler:在队列(workQueue)和线程池达到最大线程数(maximumPoolSize)均满时仍有任务的情况下的处理方
四个拒绝策略:
ThreadPoolExecutor.AbortPolicy():丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy():丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy():由调用线程( 直接调用run方法并且阻塞执行)处理该任务
查看java.util.concurrent.ThreadPoolExecutor类的源码:
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常。测试代码:
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.SECONDS, queue, factory);
while (true){
executor.submit(()->{
System.out.println(queue.size());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}

线程池拒绝策略DiscardPolicy,丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。
测试代码:
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.DiscardPolicy());
while (true){
executor.submit(()->{
System.out.println(queue.size());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}

线程池拒绝策略DiscardOldestPolicy,丢弃队列最前面的任务,然后重新提交被拒绝的任务。
此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量
测试代码:
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.DiscardOldestPolicy());
while (true){
executor.submit(()->{
System.out.println(queue.size());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
线程池拒绝策略CallerRunsPolicy,由调用线程处理该任务
如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,我们可以通过代码来验证这一点:
测试代码:
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = f -> new Thread(f, "thread-pool-test");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
try {
System.out.println(Thread.currentThread().getName() + ":执行任务");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}

通过结果可以看到,主线程main也执行了任务,这正说明了此拒绝策略由调用线程(提交任务的线程)直接执行被丢弃的任务的。
二、Executors的方式
1、使用方法
Executors类提供了下面的构造方法,

前面说Executors类通过ThreadPoolExecutor类的方式实现的,但是我们看到的返回值类型确实ExecutorService,这不是我们要的ThreadPoolExecutor,那么我们查看下他们之间的关系

可以看到ThreadPoolExecutor继承了AbstractExecutorService,AbstractExecutorService抽象类实现了ExecutorService接口,那么ThreadPoolExcutor和ExecutorService就有了关系。
那么我们在查看ExecutorService 的具体实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到返回的是ThreaPoolExecutor对象,调用的是ThreaPoolExecutor类的构造方法。
newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活收回空闲的线程,若无回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程并发数,超过的线程会在队列中等待。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int temp = i;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date) + "---"+ Thread.currentThread().getName() + " i=" + temp);
}
});
}
执行结果:
2020-01-09 15:47:20---pool-1-thread-2 i=1
2020-01-09 15:47:20---pool-1-thread-1 i=0
2020-01-09 15:47:20---pool-1-thread-3 i=2
2020-01-09 15:47:21---pool-1-thread-2 i=3
2020-01-09 15:47:21---pool-1-thread-1 i=4
2020-01-09 15:47:21---pool-1-thread-3 i=5
2020-01-09 15:47:22---pool-1-thread-1 i=7
2020-01-09 15:47:22---pool-1-thread-3 i=8
2020-01-09 15:47:22---pool-1-thread-2 i=6
2020-01-09 15:47:23---pool-1-thread-1 i=9
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
public class Test {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(sdf.format(new Date()) + "-----" + Thread.currentThread().getName()+"---" + "HeartBeat.........................");
}
}, 5, 3, TimeUnit.SECONDS); //5秒后第一次执行,之后每隔3秒执行一次
}
}
执行结果:
2020-01-09 16:13:53-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:13:56-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:13:59-----pool-1-thread-2---HeartBeat.........................
2020-01-09 16:14:02-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:14:05-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:08-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:11-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:14-----pool-1-thread-3---HeartBeat.........................
newSingleThreadExecutor 创建一个单线程化的线程池,它会用唯一的工作线程来执行任务,保证任务按照先进先出的顺序执行。
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int temp = i;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " i=" + temp);
}
});
}
}
}
执行结果
pool-1-thread-1 i=0
pool-1-thread-1 i=1
pool-1-thread-1 i=2
pool-1-thread-1 i=3
pool-1-thread-1 i=4
pool-1-thread-1 i=5
pool-1-thread-1 i=6
pool-1-thread-1 i=7
pool-1-thread-1 i=8
pool-1-thread-1 i=9

4339

被折叠的 条评论
为什么被折叠?



