1、Preface序言
线程(英语:thread)是操作系统能够进行运算调度的最小单位。
线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。
线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的降低频繁创建和销毁线程对性能所带来的开销。
Executors线程工厂类一共可以创建四种类型的线程池,通过Executors.newXXX即可创建。
ThreadPoolExecutor提供了四个构造函数
//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
在Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,学习Java中的线程池,就是对ThreadPoolExecutor构造函数的参数的配置和学习。
int corePoolSize:该线程池中核心线程数最大值
int maximumPoolSize: 该线程池中线程总数最大值
long keepAliveTime:该线程池中非核心线程闲置超时时长
TimeUnit unit:keepAliveTime的单位
BlockingQueue workQueue:该线程池中的任务队列:维护着等待执行的Runnable对象
接下来用代码案例来真实展示常见线程池的创建过程以及如何运用,看一千次不如实践一次,try!!
2、FixedThreadPool
可重用固定线程数的线程池
newFixedThreadPool:创建一个定长的线程池,可控制线程的最大并发数,超出的线程在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。
核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。
/**
* Created by 辰兮 2020/10/10
* newFixedThreadPool 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
*/
public class NewFixedThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++)
{
final int index = i;
//1- 在未来某个时间执行给定的命令。
// 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
threadRunMethod(index);
}
});
//2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
// 该 Future 的 get 方法在成功完成时将会返回给定的结果
fixedThreadPool.submit(new Runnable() {
@Override
public void run() {
threadRunMethod(index);
}
});
}
fixedThreadPool.shutdown();
}
/**
*
* @param index
*/
private static void threadRunMethod(int index) {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
0
0
1
1
2
2
3
3
4
4
Process finished with exit code 0
3、CachedThreadPool
可缓存的线程池(ps:速度快)
CachedThreadPool 缓存线程池,只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool:创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
/**
* Created by 辰兮 2020/10/10
* newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,
* 若无可回收,则新建线程。线程池的规模不存在限制。
*/
public class NewCachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1- 在未来某个时间执行给定的命令。
// 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
//2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
// 该 Future 的 get 方法在成功完成时将会返回给定的结果
cachedThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
cachedThreadPool.shutdown();
}
}
0
0
1
1
2
2
3
3
4
4
Process finished with exit code 0
4、SingleThreadExecutor
单线程线程池(ps:速度相对较慢)
通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。干活效率慢。
public static ExecutorService newSingleThreadExecutor(){
return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
它只会创建一条工作线程处理任务;
采用的阻塞队列为LinkedBlockingQueue;
/**
* Created by 辰兮 2020.10.10
* newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
* 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
*/
public class NewSingleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int index = i;
/*singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("newSingleThreadExecutor: " + index);
Thread.sleep(2*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});*/
singleThreadExecutor.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("newSingleThreadExecutor: " + index);
Thread.sleep(1*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
}
}
newSingleThreadExecutor: 0
newSingleThreadExecutor: 1
newSingleThreadExecutor: 2
newSingleThreadExecutor: 3
newSingleThreadExecutor: 4
Process finished with exit code 0
5、ScheduledThreadPool
调度线程池
它用来处理延时任务或定时任务。
/**
* Creates a new {@code ScheduledThreadPoolExecutor} with the
* given core pool size.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
这个线程池是唯一具有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。
/**
* Created by 辰兮 2020/10/10
* newScheduledThreadPool 创建一个固定长度线程池,支持定时及周期性任务执行。
*/
public class NewScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//testSchedule(scheduledExecutorService);
//testScheduleAtFixedRate(scheduledExecutorService);
testScheduleWithFixedDelay(scheduledExecutorService);
// 终止线程池
//scheduledExecutorService.shutdown();
}
/**
*
* 跟 testScheduleAtFixedRate 非常类似,就是延迟的时间有点区别
* 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
* 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
* 接着在 initialDelay + 2 * period 后执行,依此类推。
*
* 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
* 推迟的时间 : 等到上次的任务执行完之后再延迟period 的时间后执行。
* @param scheduledExecutorService
*/
private static void testScheduleWithFixedDelay(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
System.out.println("延迟2秒,再3秒执行一次");
//如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
//本次任务执行完后下次的任务还需要延迟period时间后再执行
Thread.sleep(6*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,3,TimeUnit.SECONDS);
}
/**
* 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
* 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
* 接着在 initialDelay + 2 * period 后执行,依此类推。
*
* 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
* 推迟的时间 : 等到上次的任务执行完就立马执行。
* @param scheduledExecutorService
*/
private static void testScheduleAtFixedRate(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
System.out.println("延迟2秒,再3秒执行一次");
//如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
//如果任务里面执行的时间大于 period 的时间,本次任务执行完后,下次任务立马执行。
Thread.sleep(6*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,3,TimeUnit.SECONDS);
}
/**
* 创建并执行在给定延迟后启用的一次性操作
* @param scheduledExecutorService
*/
private static void testSchedule(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
}
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
....
线程池的好处
1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3、提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源。
阿里巴巴Java手册:为什么不建议使用Executors创建线程池?
不建议采用Executors创建线程池,但是可以自定义线程池相关参数创建!
第一次整理关于线程池相关知识,如有不对欢迎大佬们指导!
The best investment is to invest in yourself.
愿你们奔赴在自己的热爱里!