目录
一、线程池Executor
1. 传统 new Thread
的弊端
- 每次
new Thread
新建对象,性能差; - 线程缺乏统一管理,可能无限制的新建线程,相互竞争,可能占用过多的系统资源导致死机或者
OOM
(out of memory
内存溢出),这种问题的原因不是因为单纯的new
一个Thread
,而是可能因为程序的bug或者设计上的缺陷导致不断new Thread
造成的。 - 缺少更多功能,如更多执行、定期执行、线程中断。
因此日常使用线程的时候,尽量避免使用new Thread
去创建线程,从而减少不必要的麻烦和困扰;
2. 线程池的优点
- 重用存在的线程,减少对象创建、消亡的开销,性能好;
- 可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
3.线程池(ThreadPoolExecutor
)的核心类
参数说明:ThreadPoolExecutor
一共有七个参数,这七个参数配合起来,构成了线程池强大的功能。
corePoolSize
:核心线程数量;maximumPoolSize
:线程最大线程数;workQueue
:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响;keepAliveTime
:线程没有任务执行时最多保持多久时间终止(当线程中的线程数量大于corePoolSize
的时候,如果这时没有新的任务提交核心线程外的线程不会立即销毁,而是等待,直到超过keepAliveTime
);unit
:keepAliveTime
的时间单位;threadFactory
:线程工厂,用来创建线程,有一个默认的工场来创建线程,这样新创建出来的线程有相同的优先级,是非守护线程、设置好了名称);rejectHandler
:当拒绝处理任务时(阻塞队列满)的策略(AbortPolicy
默认策略直接抛出异常、CallerRunsPolicy
用调用者所在的线程执行任务、DiscardOldestPolicy
丢弃队列中最靠前的任务并执行当前任务、DiscardPolicy
直接丢弃当前任务)
4.corePoolSize
、maximumPoolSize
、workQueue
三者关系
如果运行的线程数小于corePoolSize
的时候,直接创建新线程来处理任务。即使线程池中的其他线程是空闲的。如果运行中的线程数大于corePoolSize
且小于maximumPoolSize
时,那么只有当workQueue
满的时候才创建新的线程去处理任务。如果corePoolSize
与maximumPoolSize
是相同的,那么创建的线程池大小是固定的。这时有新任务提交,当workQueue
未满时,就把请求放入workQueue
中。等待空线程从workQueue
取出任务。如果workQueue
此时也满了,那么就使用另外的拒绝策略参数去执行拒绝策略。
5.线程池的五种状态
- running:能接受新提交的任务,也能处理阻塞队列中的任务
- shutdown:不能处理新的任务,但是能继续处理阻塞队列中任务
- stop:不能接收新的任务,也不处理队列中的任务
- tidying:如果所有的任务都已经终止了,这时有效线程数为0
- terminated:最终状态
6.线程池ThreadPoolExecutor
的主要方法
execute()
:提交任务,交给线程池执行;submit()
:提交任务,能够返回执行结果execute+Future
;shutdown()
:关闭线程池,等待任务都执行完;shutdownNow()
:关闭线程池,不等待任务执行完;getTaskCount()
:线程池已执行和未执行的任务总数;getCompleteTaskCount()
:已完成的任务数量;getPoolSize()
:线程池当前的线程数量;getActiveCount()
:当前线程池中正在执行任务的线程数量;
二、使用Executor
创建线程池
注意:ExecutorService
中只提供了基本的线程池的方法:submit()
、 shutdown()
、shutdownNow()
等;而不支持其他监控方法,如getTaskCount()
、getCompleteTaskCount()
等;
1.Executors.newCachedThreadPool()
方法创建线程池
Executors.newCachedThreadPool()
其目的是创建一个可缓存的线程池,如果线程池的长度超过了处理的需要,可以灵活回收空闲线程。如果没有可回收的就新建线程。
newCachedThreadPool
源码如下所示:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
由此可见,通过使用Executors
中的newCachedThreadPool()
方法创建线程池,其内部实际还是通过ThreadPoolExecutor
来创建线程池,只不过很多参数都已经被指定好了。
- 具体使用示例:
@Slf4j
public class ThreadPoolExample1 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
}
}
执行结果:
16:12:57.979 [pool-1-thread-9] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:8
16:12:57.985 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:0
16:12:57.979 [pool-1-thread-2] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:1
16:12:57.980 [pool-1-thread-4] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:3
16:12:57.980 [pool-1-thread-10] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:9
16:12:57.980 [pool-1-thread-3] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:2
16:12:57.980 [pool-1-thread-6] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:5
16:12:57.979 [pool-1-thread-5] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:4
16:12:57.980 [pool-1-thread-8] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:7
16:12:57.980 [pool-1-thread-7] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample1 - task:6
Process finished with exit code 0
2.Executors.newFixedThreadPool()
方法创建线程池
其目的是创建定长线程池,可以线程现成的最大并发数,超出在队列等待
- 源码解读:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 具体使用示例:
@Slf4j
public class ThreadPoolExample2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
}
}
执行结果同上;
3.Executors.newSingleThreadExecutor()
方法创建线程池
其目的是创建单线程化的线程池,用唯一的一个共用线程执行任务,保证所有任务按指定顺序执行(FIFO、优先级…)
- 源码解读:
//源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 使用示例:
@Slf4j
public class ThreadPoolExample3 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
}
}
执行结果如下:
16:34:57.825 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:0
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:1
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:2
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:3
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:4
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:5
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:6
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:7
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:8
16:34:57.832 [pool-1-thread-1] INFO com.mmall.concurrency.example.threadPool.ThreadPoolExample3 - task:9
Process finished with exit code 0
执行得到结果与预期得到一致,线程按照顺序依次执行;
4.Executors.newScheduledThreadPool()
方法创建线程池
其目的是创建定长线程池,支持定时和周期任务执行;
- 源码解读:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,//此处super指的是ThreadPoolExecutor
new DelayedWorkQueue());
}
- 使用示例:
@Slf4j
public class ThreadPoolExample4 {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
// executorService.schedule(new Runnable() {
// @Override
// public void run() {
// log.warn("schedule run");
// }
// }, 3, TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 1, 3, TimeUnit.SECONDS);
// executorService.shutdown();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
log.warn("timer run");
}
}, new Date(), 5 * 1000);
}
}
Java并发编程学习系列
如有帮助,烦请点赞收藏一下啦 (◕ᴗ◕✿)