简而言之,线程池就是管理线程的一个容器,有任务需要处理时,会相继判断核心线程数是否还有空闲、线程池中的任务队列是否已满、是否超过线程池大小,然后调用或创建线程或者排队,线程执行完任务后并不会立即被销毁,而是仍然在线程池中等待下一个任务,如果超过存活时间还没有新的任务就会被销毁,通过这样复用线程从而降低开销。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
状态说明 :线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建(比如调用Executors.newFixedThreadPool()或者使用ThreadPoolExecutor进行创建),就处于RUNNING状态,并且线程池中的任务数为0!
状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) ->STOP。
状态说明:当所有的任务已终止,记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池 变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING,当线程池在STOP状态下,线程池中执行的任务为空时,会由STOP -> TIDYING。
状态说明:线程池彻底终止,就变成TERMINATED状态。
状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
//核心线程数
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 :核心线程数,一直保持的线程的数量,即使线程空闲也不会释放。除非设置了 allowCoreThreadTimeout 为 true;
设置规则:
CPU密集型:(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1
IO密集型:(与CPU密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2
maximumPoolSize:最大线程数,队列满时开启新线程直到等于该值;
设置规则:
keepAliveTime:线程空闲时间,即线程池没任务的话,线程存活多久就销毁
设置规则:
unit:线程空闲时间单位
设置规则:
workQueue:缓存任务的队列。实现 BlockingQueue。LinkedBlockingQueue:用链表实现的队列,可以是有界的,也可以是无界的,但在Executors中默认使用无界的。
ArrayBlockingQueue
LinkedBlockingQueue
DelayQueue
PriorityBlockingQueue
SynchronousQueue
设置规则:
ArrayBlockingQueue
LinkedBlockingQueue
DelayQueue
PriorityBlockingQueue
SynchronousQueue
threadFactory :线程工厂,一般我们用来给线程命名或者做一些定制时使用
设置规则:
handler :拒绝策略,即当线程池满了,新来的任务怎么处理,ThreadPooLExecutor自带四种
设置规则:
序号 | 类型 | 说明 |
---|---|---|
1 | newFixedThreadPool | 创建⼀个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待 |
2 | newCachedThreadPool | 创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程 |
3 | newSingleThreadExecutor | 创建单个线程数的线程池,它可以保证先进先出的执⾏顺序 |
4 | newScheduledThreadPool | 创建⼀个可以执行延迟任务的线程池 |
5 | newSingleThreadScheduledExecutor | 创建⼀个单线程的可以执行延迟任务的线程池 |
6 | newWorkStealingPool | 创建⼀个抢占式执行的线程池(任务执⾏顺序不确定)【JDK1.8 添加】 |
public static void main1(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
try {
for (int i = 0; i < 5; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(" fixedThreadPool.execute---" + Thread.currentThread().getName());
}
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//线程池用完,程序结束,关闭线程池(为确保关闭,将关闭方法放入到finally中)
fixedThreadPool.shutdown();
}
}
//运行结果
fixedThreadPool.execute线程池---pool-1-thread-2
fixedThreadPool.execute线程池---pool-1-thread-1
fixedThreadPool.execute线程池---pool-1-thread-3
fixedThreadPool.execute线程池---pool-1-thread-3
fixedThreadPool.execute线程池---pool-1-thread-1
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
try {
for (int i = 0; i < 5; i++) {
Future<Integer> future = fixedThreadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(9);
System.out.println("随机数-----" + num +"---fixedThreadPool.submit---"+Thread.currentThread().getName());
return num;
}
});
System.out.println("结果---" +future.get());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//线程池用完,程序结束,关闭线程池(为确保关闭,将关闭方法放入到finally中)
fixedThreadPool.shutdown();
}
}
//运行结果
随机数-----5---fixedThreadPool.submit---pool-1-thread-1
结果---5
随机数-----1---fixedThreadPool.submit---pool-1-thread-2
结果---1
随机数-----5---fixedThreadPool.submit---pool-1-thread-3
结果---5
随机数-----4---fixedThreadPool.submit---pool-1-thread-1
结果---4
随机数-----5---fixedThreadPool.submit---pool-1-thread-2
结果---5
execute和submit的区别
1.execute和submit都属于线程池的方法,execute只能提交Runnable类型的任务
2.submit既能提交Runnable类型任务也能提交Callable类型任务。
3.execute()没有返回值
4.submit有返回值,所以需要返回值的时候必须使用submit
- 核心线程数和最大线程数大小一样
- 没有所谓的非空闲时间,即keepAliveTime为0
- 阻塞队列为无界队列LinkedBlockingQueue
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println(" newCachedThreadPool.execute---" + Thread.currentThread().getName());
});
}
}
//运行结果
newCachedThreadPool.execute---pool-1-thread-1
newCachedThreadPool.execute---pool-1-thread-5
newCachedThreadPool.execute---pool-1-thread-4
newCachedThreadPool.execute---pool-1-thread-2
newCachedThreadPool.execute---pool-1-thread-3
特点&注意点
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
- 核心线程数为0
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是SynchronousQueue
- 非核心线程空闲存活时间为60秒
当提交任务的速度大于处理任务的速度时,每次提交一个任务,就必然会创建一个线程。极端情况下会创建过多的线程,耗尽 CPU 和内存资源。由于空闲 60 秒的线程会被终止,长时间保持空闲的 CachedThreadPool 不会占用任何资源。
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.execute(()-> {
System.out.println(" newCachedThreadPool.execute---" + Thread.currentThread().getName());
});
}
}
//运行结果
newCachedThreadPool.execute---pool-1-thread-1
newCachedThreadPool.execute---pool-1-thread-1
newCachedThreadPool.execute---pool-1-thread-1
newCachedThreadPool.execute---pool-1-thread-1
newCachedThreadPool.execute---pool-1-thread-1
特点&注意点
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
- 核心线程数为1
- 最大线程数也为1
- 阻塞队列是LinkedBlockingQueue
- keepAliveTime为0
public static void main(String[] args) {
/**
* 创建一个给定初始延迟的间隔性的任务,之后的下次执行时间是上一次任务从执行到结束所需要的时间+给定的间隔时间
*/
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
scheduledThreadPool.scheduleWithFixedDelay(()->{
System.out.println("current Time" + System.currentTimeMillis());
System.out.println(" scheduledThreadPool.scheduleWithFixedDe lay---" + Thread.currentThread().getName());
}, 1, 3, TimeUnit.SECONDS);
}
//运行结果
current Time1693668943517
scheduledThreadPool.scheduleWithFixedDelay---pool-1-thread-1
current Time1693668946523
scheduledThreadPool.scheduleWithFixedDelay---pool-1-thread-1
current Time1693668949534
scheduledThreadPool.scheduleWithFixedDelay---pool-1-thread-1
current Time1693668952536
scheduledThreadPool.scheduleWithFixedDelay---pool-1-thread-1
特点&注意点
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize,
Integer.MAX_VALUE,
0,
NANOSECONDS,
new DelayedWorkQueue(),
threadFactory);
}
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是DelayedWorkQueue
- keepAliveTime为0
- scheduleAtFixedRate() :按某种速率周期执行
- scheduleWithFixedDelay():在某个延迟后执行