Java线程池(超详细)

article/2025/10/9 13:12:52

文章目录

    • 1. 线程池概念
    • 2. JUC线程池架构
    • 3. Executors创建线程的4种方法
    • 4. 线程池的标准创建方式
    • 5. 向线程池提交任务的两种方式
    • 6. 线程池的任务调度流程
    • 7. ThreadFactory(线程工厂)
    • 8. 任务阻塞队列
    • 9. 调度器的钩子方法
    • 10. 线程池的拒绝策略
    • 11. 线程池的关闭

1. 线程池概念

创建Java线程需要给线程分配堆栈内存以及初始化内存,还需要进行系统调用,频繁地创建和销毁线程会大大降低系统的运行效率,采用线程池来管理线程有以下好处:

  1. 提升性能:线程池能独立负责线程的创建、维护和分配
  2. 线程管理:每个Java线程池会保持一些基本的线程统计信息,对线程进行有效管理

2. JUC线程池架构

在这里插入图片描述

1. Executor
Executor提供了execute()接口来执行已提交的Runnable执行目标实例,它只有1个方法:
void execute(Runnable command)

2. ExecutorService
继承于Executor,Java异步目标任务的“执行者服务接”口,对外提供异步任务的接收服务

 	* @param task the task to submit* @param <T> the type of the task's result* @return a Future representing pending completion of the task* @throws RejectedExecutionException if the task cannot be*         scheduled for execution* @throws NullPointerException if the task is null*/<T> Future<T> submit(Callable<T> task);//向线程池提交单个异步任务
//想线程池提交批量异步任务<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException;

3. AbstractExecutorService
抽象类,实现了ExecutorService

4. ThreadPoolExecutor
线程池实现类,继承于AbstractExecutorService,JUC线程池的核心实现类

5. ScheduledExecutorService
继承于ExecutorService。它是一个可以完成“延时”和“周期性”任务的调度线程池接口

6. ScheduledThreadPoolExecutor
继承于ThreadPoolExecutor,实现了ExecutorService中延时执行和周期执行等抽象方法

7. Executors
静态工厂类,它通过静态工厂方法返回ExecutorServiceScheduledExecutorService等线程池示例对象


3. Executors创建线程的4种方法

1. newSingleThreadExecutor创建“单线程化线程池”

package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);private String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){System.out.println("task:"+taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("task:"+taskName+" end...");}}public static void main(String[] args) {ExecutorService pool=Executors.newSingleThreadExecutor();for(int i=0;i<3;i++){pool.execute(new TargetTask());pool.submit(new TargetTask());}pool.shutdown();}}

在这里插入图片描述

特点:

  • 单线程化的线程池中的任务是按照提交的次序顺序执行的
  • 只有一个线程的线程池
  • 池中的唯一线程的存活时间是无限的
  • 当池中的唯一线程正繁忙时,新提交的任务实例会进入内部的阻塞队列中,并且其阻塞队列是无界的
  • 适用场景:任务按照提交次序,一个任务一个任务地逐个执行的场景

2. newFixedThreadPool创建“固定数量的线程池

package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);private String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){System.out.println(taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}public static void main(String[] args) {ExecutorService pool=Executors.newFixedThreadPool(3);//创建含有3个线程的线程池for(int i=0;i<5;i++){pool.execute(new TargetTask());pool.submit(new TargetTask());}pool.shutdown();}}

在这里插入图片描述

特点:

  • 如果线程数没有达到“固定数量”,每次提交一个任务线程池内就创建一个新线程,直到线程达到线程池固定的数量
  • 线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
  • 在接收异步任务的执行目标实例时,如果池中的所有线程均在繁忙状态,新任务会进入阻塞队列中(无界的阻塞队列)

适用场景:

  • 需要任务长期执行的场景
  • CPU密集型任务

缺点:

  • 内部使用无界队列来存放排队任务,当大量任务超过线程池最大容量需要处理时,队列无限增大,使服务器资源迅速耗尽

3. newCachedThreadPool创建“可缓存线程池”

package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);private String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){System.out.println(taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}public static void main(String[] args) {ExecutorService pool=Executors.newCachedThreadPool();for(int i=0;i<5;i++){pool.execute(new TargetTask());pool.submit(new TargetTask());}pool.shutdown();}}

在这里插入图片描述

特点:

  • 在接收新的异步任务target执行目标实例时,如果池内所有线程繁忙,此线程池就会添加新线程来处理任务
  • 线程池不会对线程池大小进行限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
  • 如果部分线程空闲,也就是存量线程的数量超过了处理任务数量,就会回收空闲(60秒不执行任务)线程

适用场景:

  • 需要快速处理突发性强、耗时较短的任务场景,如Netty的NIO处理场景、REST API接口的瞬时削峰场景

缺点:

  • 线程池没有最大线程数量限制,如果大量的异步任务执行目标实例同时提交,可能会因创建线程过多而导致资源耗尽

4. newScheduledThreadPool创建“可调度线程池”

  • 延时性
  • 周期性
package threadpool;
import java.security.Policy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);private String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){System.out.println(taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}public static void main(String[] args) throws InterruptedException {ScheduledExecutorService pool=Executors.newScheduledThreadPool(2);for(int i=0;i<2;i++){pool.scheduleAtFixedRate(new TargetTask(), 0, 500, TimeUnit.MILLISECONDS);//参数1: task任务  //参数2: 首次执行任务的延迟时间//参数3: 周期性执行的时间//参数4: 时间单位}Thread.sleep(3000);//主线程睡眠时间越长 周期次数越多pool.shutdown();}}

在这里插入图片描述

总结:Executors创建线程池的4种方法十分方便,但是构造器创建普通线程池、可调度线程池比较复杂,这些构造器会涉及大量的复杂参数,已经较少使用。

Executors创建线程池存在的问题:

  1. 创建固定数量线程池的问题
  public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}

阻塞队列无界,队列很大,很有可能导致JVM出现OOM(Out Of Memory)异常,即内存资源耗尽

  1. 创建单线程线程池的问题
 public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}

问题和固定数量线程池一样,阻塞队列无界

  1. 创建缓存线程池的问题
 public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

问题存在于其最大线程数量不设限上。由于其maximumPoolSize的值为Integer.MAX_VALUE(非常大),可以认为可以无限创建线程,如果任务提交较多,就会造成大量的线程被启动,很有可能造成OOM异常,甚至导致CPU线程资源耗尽

  1. 创建可调度线程存在的问题
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}

主要问题在于线程数不设上限

总结:

  • newFixedThreadPool和newSingleThreadExecutor: 阻塞队列无界,会堆积大量任务导致OOM(内存耗尽)
  • newCachedThreadPool和newScheduledThreadPool: 线程数量无上界,会导致创建大量的线程,从而导致OOM
  • 建议直接使用线程池ThreadPoolExecutor的构造器

4. 线程池的标准创建方式

public class ThreadPoolExecutor extends AbstractExecutorService {/*** Core pool size is the minimum number of workers to keep alive* (and not allow to time out etc) unless allowCoreThreadTimeOut* is set, in which case the minimum is zero.*/private volatile int corePoolSize;//核心线程数,即使线程空闲也不会被收回/*** Maximum pool size. Note that the actual maximum is internally* bounded by CAPACITY.*/private volatile int maximumPoolSize;//线程的上限/*** Timeout in nanoseconds for idle threads waiting for work.* Threads use this timeout when there are more than corePoolSize* present or if allowCoreThreadTimeOut. Otherwise they wait* forever for new work.*/private volatile long keepAliveTime;//线程的最大空闲时长/*** The queue used for holding tasks and handing off to worker* threads.  We do not require that workQueue.poll() returning* null necessarily means that workQueue.isEmpty(), so rely* solely on isEmpty to see if the queue is empty (which we must* do for example when deciding whether to transition from* SHUTDOWN to TIDYING).  This accommodates special-purpose* queues such as DelayQueues for which poll() is allowed to* return null even if it may later return non-null when delays* expire.*/private final BlockingQueue<Runnable> workQueue;//任务的排队队列private volatile ThreadFactory threadFactory;//新线程的产生方式/*** Handler called when saturated or shutdown in execute.*/private volatile RejectedExecutionHandler handler;//拒绝策略
}

1. 核心线程和最大线程数量

  • corePoolSize用于设置核心(Core)线程池数量,参数maximumPoolSize用于设置最大线程数量
  • 线程池接收到新任务,当前工作线程数少于corePoolSize, 即使有空闲的工作线程,也会创建新的线程来处理该请求,直到线程数达到corePoolSize
  • 当前工作线程数多于corePoolSize数量,但小于maximumPoolSize数量,那么仅当任务排队队列已满时才会创建新线程
  • maximumPoolSize被设置为无界值(如Integer.MAX_VALUE)时,线程池可以接收任意数量的并发任务

2. BlockingQueue

  • BlockingQueue(阻塞队列)的实例用于暂时接收到的异步任务,如果线程池的核心线程都在忙,那么所接收到的目标任务缓存在阻塞队列中

3. keepAliveTime

  • 空闲线程存活时间
  • 用于设置池内线程最大Idle(空闲)时长(或者说保活时长)
  • 超过这个时间,默认情况下Idle、非Core线程会被回收

注意:若调用了allowCoreThreadTimeOut(boolean)方法,并且传入了参数true,则keepAliveTime参数所设置的Idle超时策略也将被应用于核心线程


5. 向线程池提交任务的两种方式

  1. execute方法
    void execute(Runnable command): Executor接口中的方法
  2. submit方法
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    这3个submit方法都是ExecutorService接口中的方法

两种方法的区别:

  • execute()方法只能接收Runnable类型的参数,而submit()方法可以接收Callable、Runnable两种类型的参数
  • Callable类型的任务是可以返回执行结果的,而Runnable类型的任务不可以返回执行结果
  • submit()提交任务后会有返回值,而execute()没有
  • submit()方便Exception处理

1. 通过submit()返回的Future对象获取结果

package threadpool;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static void main(String[] args) throws InterruptedException {ScheduledExecutorService pool=Executors.newScheduledThreadPool(2);Future<Integer> future=pool.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {return 123;}});try {Integer result=future.get();System.out.println("result:"+result);//123} catch (ExecutionException e) {e.printStackTrace();}Thread.sleep(1000);pool.shutdown();}}

2. 通过submit()返回的Future对象捕获异常

package threadpool;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;import javax.management.RuntimeErrorException;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){System.out.println(taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}static class TargetTaskWithError extends TargetTask{public void run(){super.run();//执行父类的run方法throw new RuntimeException("Error from "+taskName);}}public static void main(String[] args) throws InterruptedException {ScheduledExecutorService pool=Executors.newScheduledThreadPool(2);pool.execute(new TargetTaskWithError());Future future=pool.submit(new TargetTaskWithError());try {if(future.get()==null){System.out.println("No Exception");}} catch (ExecutionException e) {e.printStackTrace();}Thread.sleep(1000);pool.shutdown();}}

在这里插入图片描述

execute()方法在启动任务执行后,任务执行过程中可能发生的异常调用者并不关心。而通过submit()方法返回的Future对象(异步执行实例),可以进行异步执行过程中的异常捕获


6. 线程池的任务调度流程

在这里插入图片描述

  1. 如果当前工作线程数量小于核心线程数量,执行器总是优先创建一个任务线程,而不是从线程队列中获取一个空闲线程
  2. 如果线程池中总的任务数量大于核心线程池数量,新接收的任务将被加入阻塞队列中,一直到阻塞队列已满。
  3. 当完成一个任务的执行时,执行器总是优先从阻塞队列中获取下一个任务,并开始执行,一直到阻塞队列为空
  4. 核心线程池数量已经用完阻塞队列也已经满了的场景下,如果线程池接收到新的任务,将会为新任务创建一个线程(非核心线程),并且立即开始执行新任务
  5. 核心线程都用完阻塞队列已满的情况下,一直会创建新线程去执行新任务,直到池内的线程总数超出maximumPoolSize。如果线程池的线程总数超过maximumPoolSize,线程池就会拒绝接收任务,当新任务过来时,会为新任务执行拒绝策略

注意点:

  • 核心和最大线程数量、BlockingQueue队列等参数如果配置得不合理,可能会造成异步任务得不到预期的并发执行,造成严重的排队等待现象
  • 线程池的调度器创建线程的一条重要的规则是:在corePoolSize已满之后,还需要等阻塞队列已满,才会去创建新的线程

example: 设置核心线程数量为1,阻塞队列为100,有5个任务待执行(假设极端情况下任务一直执行不接受),则只有1个任务可以被执行,其他4个任务在阻塞队列中,而不是创建新线程进行处理(阻塞队列未满)


7. ThreadFactory(线程工厂)

public interface ThreadFactory {/*** Constructs a new {@code Thread}.  Implementations may also initialize* priority, name, daemon status, {@code ThreadGroup}, etc.** @param r a runnable to be executed by new thread instance* @return constructed thread, or {@code null} if the request to*         create a thread is rejected*/Thread newThread(Runnable r);
}
  • ThreadFactory是Java线程工厂接口,只有1个方法,调用ThreadFactory的唯一方法newThread()创建新线程时,可以更改所创建的新线程的名称、线程组、优先级、守护进程状态等
  • 使用Executors创建新的线程池时,可以指定工厂,未指定是默认使用线程池时,也可以基于ThreadFactory(线程工厂)创建,在创建新线程池时可
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory);public static ExecutorService newFixedThreadPool(int nThreads) 
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){   System.out.println(Thread.currentThread().getName()+": "+taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}static class SimpleThreadFactory implements ThreadFactory{static AtomicInteger threadNo=new AtomicInteger(1);public Thread newThread(Runnable task) {String threadName="simpleThread-"+threadNo;System.out.println("创建一条线程,名字是:"+threadName);threadNo.incrementAndGet();Thread thread=new Thread(task,threadName);thread.setDaemon(true);return thread;}public static void main(String[] args) throws InterruptedException {ExecutorService pool=Executors.newFixedThreadPool(2,new SimpleThreadFactory());// ExecutorService pool=Executors.newFixedThreadPool(2);for(int i=0;i<5;i++){pool.submit(new TargetTask());}Thread.sleep(5000);pool.shutdown();}}
}

在这里插入图片描述
使用默认线程工厂的情况如下:

public static void main(String[] args) throws InterruptedException {//ExecutorService pool=Executors.newFixedThreadPool(2,new SimpleThreadFactory());ExecutorService pool=Executors.newFixedThreadPool(2);for(int i=0;i<5;i++){pool.submit(new TargetTask());}Thread.sleep(5000);pool.shutdown();

在这里插入图片描述

线程工厂和线程池工厂:
Executors为线程池工厂类,用于快捷创建线程池(Thread Pool);ThreadFactory为线程工厂类,用于创建线程(Thread)


8. 任务阻塞队列

特点:在一个线程从一个空的阻塞队列中获取元素时线程会被阻塞,直到阻塞队列中有了元素;当队列中有元素后,被阻塞的线程会自动被唤醒

常见的几种阻塞队列的实现:

  1. ArrayBlockingQueue:是一个数组实现的有界阻塞队列(有界队列),队列中的元素按FIFO排序,ArrayBlockingQueue在创建时必须设置大小
  2. LinkedBlockingQueue:是一个基于链表实现的阻塞队列,按FIFO排序任务,可以设置容量(有界队列),不设置容量则默认使用Integer.Max_VALUE作为容量(无界队列)
  3. PriorityBlockingQueue:是具有优先级的无界队列

9. 调度器的钩子方法

三个钩子方法存在于ThreadPoolExecutor类,这3个方法都是空方法,一般会在子类中重写

protected void beforeExecute(Thread t, Runnable r) { }: 任务执行之前的钩子方法
protected void afterExecute(Runnable r, Throwable t) { }: 任务执行之后的钩子方法
protected void terminated() { }: 线程池终止时的钩子方法

package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){   System.out.println(Thread.currentThread().getName()+": "+taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}static class SimpleThreadFactory implements ThreadFactory{static AtomicInteger threadNo=new AtomicInteger(1);public Thread newThread(Runnable task) {String threadName="simpleThread-"+threadNo;System.out.println("创建一条线程,名字是:"+threadName);threadNo.incrementAndGet();Thread thread=new Thread(task,threadName);thread.setDaemon(true);return thread;}public static void main(String[] args) throws InterruptedException {ExecutorService pool=new ThreadPoolExecutor(2, 4, 60,TimeUnit.SECONDS, new LinkedBlockingQueue<>(2)){@Overrideprotected void terminated(){System.out.println("调度器已停止...");}@Overrideprotected void beforeExecute(Thread t,Runnable target){System.out.println("前钩执行...");super.beforeExecute(t, target);}@Overrideprotected void afterExecute(Runnable target,Throwable t){System.out.println("后钩执行...");super.afterExecute(target, t);}};for(int i=0;i<5;i++)pool.execute(new TargetTask());Thread.sleep(5000);pool.shutdown();}}
}

在这里插入图片描述


10. 线程池的拒绝策略

拒绝情况:

  1. 线程池已经被关闭
  2. 工作队列已满且maximumPoolSize已满

几种常见的拒绝策略:
在这里插入图片描述

  1. AbortPolicy:拒绝策略

新任务就会被拒绝,并且抛出RejectedExecutionException异常。该策略是线程池默认的拒绝策略

  1. DiscardPolicy:抛弃策略

新任务就会直接被丢掉,并且不会有任何异常抛出

  1. DiscardOldestPolicy:抛弃最老任务策略

将最早进入队列的任务抛弃,从队列中腾出空间,再尝试加入队列(一般队头元素最老)

  1. CallerRunsPolicy:调用者执行策略

新任务被添加到线程池时,如果添加失败,那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去执行新任务

  1. 自定义策略
package threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class CreateThreadPollDemo {public static final int SLEEP_GAP=1000;static class TargetTask implements Runnable{static AtomicInteger taskNo=new AtomicInteger(1);String taskName;public TargetTask(){taskName="task-"+taskNo;taskNo.incrementAndGet();}public void run(){   System.out.println(Thread.currentThread().getName()+": "+taskName+" is doing...");try {Thread.sleep(SLEEP_GAP);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName+" end...");}}static class SimpleThreadFactory implements ThreadFactory{static AtomicInteger threadNo=new AtomicInteger(1);public Thread newThread(Runnable task) {String threadName="simpleThread-"+threadNo;System.out.println("创建一条线程,名字是:"+threadName);threadNo.incrementAndGet();Thread thread=new Thread(task,threadName);thread.setDaemon(true);return thread;}static class CustomerIgnorePolicy implements RejectedExecutionHandler{@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println(Thread.currentThread().getName()+"-rejected;  taskCount-"+executor.getTaskCount());}}public static void main(String[] args) throws InterruptedException {int corePoolSize=2;//核心线程数int maximumPoolSize=4;//最大线程数long keepAlive=10;//空闲时间TimeUnit unit=TimeUnit.SECONDS;//时间单位BlockingQueue<Runnable> workQueue=new LinkedBlockingQueue<>(2);//阻塞队列ThreadFactory factory=new SimpleThreadFactory();//自定义线程工厂RejectedExecutionHandler policy=new CustomerIgnorePolicy();//自定义拒绝策略ThreadPoolExecutor pool=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAlive,unit,workQueue,factory,policy);pool.prestartAllCoreThreads();for(int i=0;i<11;i++)pool.execute(new TargetTask());Thread.sleep(5000);pool.shutdown();}}
}

在这里插入图片描述


11. 线程池的关闭

线程池的5种状态:

 // runState is stored in the high-order bitsprivate static final int RUNNING    = -1 << COUNT_BITS;private static final int SHUTDOWN   =  0 << COUNT_BITS;private static final int STOP       =  1 << COUNT_BITS;private static final int TIDYING    =  2 << COUNT_BITS;private static final int TERMINATED =  3 << COUNT_BITS;
  1. RUNNING: 线程池创建之后的初始状态,这种状态下可以执行任务
  2. SHUTDOWN:该状态下线程池不再接受新任务,但是会将工作队列中的任务执行完毕
  3. STOP:该状态下线程池不再接受新任务,也不会处理工作队列中的剩余任务,并且将会中断所有工作线程
  4. TIDYING:该状态下所有任务都已终止或者处理完成,将会执行terminated()钩子方法
  5. TERMINATED:执行完terminated()钩子方法之后的状态

几种状态的转换:
在这里插入图片描述

几种关闭线程池的方法:

  1. shutdown()方法

等待当前工作队列中的剩余任务全部执行完成之后,才会执行关闭,但是此方法被调用之后线程池的状态转为SHUTDOWN,线程池不会再接收新的任务

 public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();//检测权限advanceRunState(SHUTDOWN);//设置线程池状态interruptIdleWorkers();//中断空闲线程onShutdown(); // 钩子函数,用于清理一些资源} finally {mainLock.unlock();}tryTerminate();}
  1. shutdownNow()方法

立即关闭线程池的方法,此方法会打断正在执行的工作线程,并且会清空当前工作队列中的剩余任务,返回的是尚未执行的任务

 public List<Runnable> shutdownNow() {List<Runnable> tasks;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();//检测权限advanceRunState(STOP);//设置线程池状态interruptWorkers();//中断所有线程(工作线程以及空闲线程)tasks = drainQueue();//丢弃工作队列中的剩余任务} finally {mainLock.unlock();}tryTerminate();return tasks;}
  1. awaitTermination()方法

等待线程池完成关闭, shutdown()与shutdownNow()方法之后,用户程序都不会主动等待线程池关闭完成

public boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException {long nanos = unit.toNanos(timeout);final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {for (;;) {if (runStateAtLeast(ctl.get(), TERMINATED))return true;if (nanos <= 0)return false;nanos = termination.awaitNanos(nanos);}} finally {mainLock.unlock();}}

在设置的时间timeout内如果线程池完成关闭,返回true, 否则返回false

在这里插入图片描述


参考书籍:《Java高并发编程卷2》 尼恩


http://chatgpt.dhexx.cn/article/wcR3fIvT.shtml

相关文章

Java多线程超详解

引言 随着计算机的配置越来越高&#xff0c;我们需要将进程进一步优化&#xff0c;细分为线程&#xff0c;充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说&#xff0c;今天本帅将记录自己线程的学习。 程序&#xff0c;进程&#xff0c;线程的…

java多线程(超详细)

1 - 线程 1.1 - 进程 进程就是正在运行中的程序&#xff08;进程是驻留在内存中的&#xff09; 是系统执行资源分配和调度的独立单位 每一进程都有属于自己的存储空间和系统资源 注意&#xff1a;进程A和进程B的内存独立不共享。 1.2 - 线程 线程就是进程中的单个顺序控制…

JAVA线程

一、线程相关概念 &#xff08;一&#xff09;程序、进程和线程的区别 程序 程序是含有指令和数据的文件&#xff0c;被存储在磁盘或其他的数据存储设备中&#xff0c;也就是说程序是静态的代码。 进程 进程是程序的一次执行过程&#xff0c;是系统运行的基本单位&#xf…

Java 线程 基础知识总结

线程基础 很不严谨的说&#xff0c;线程是什么&#xff1f;线程就是为了让很多个东西并发执行&#xff0c;大大的提高程序执行的效率啊 三个非常重要的概念&#xff1a; 程序&#xff1a;一组写好了的静态代码块&#xff08;就我们写的那些代码玩意&#xff09;进程&#xf…

Java多线程(超详解)

目录 1. 线程简介 1.1 程序 1.2 进程 1.3 线程 1.4 多线程 1.5 普通方法调用和多线程 2. 线程创建 2.1 继承Thread类 2.2 实现Runnable接口 2.3 实现Callable接口&#xff08;了解&#xff09; 2.4 网图下载 2.4.1 通过继承Thread类实现网图下载 2.4.2 通…

java 线程详解

一、线程的基本概念 一个程序最少需要一个进程&#xff0c;而一个进程最少需要一个线程。关系是线程–>进程–>程序的大致组成结构。所以线程是程序执行流的最小单位&#xff0c;而进程是系统进行资源分配和调度的一个独立单位。 一个线程就是在进程中的一个单一的顺序…

JAVA多线程详解(超详细)

目录 一、线程简介1、进程、线程2、并发、并行、串行3、进程的三态 二、线程实现1、继承Thread类2、实现Runnable接口3、实现Callable接口&#xff08;不常用&#xff09; 三、线程常用方法1、线程的状态2、线程常用方法 四、多线程1、守护&#xff08;Deamon&#xff09;线程2…

Java多线程(超详细!)

1、什么是进程&#xff1f;什么是线程&#xff1f; 进程是:一个应用程序&#xff08;1个进程是一个软件&#xff09;。 线程是&#xff1a;一个进程中的执行场景/执行单元。 注意&#xff1a;一个进程可以启动多个线程。 eg. 对于java程序来说&#xff0c;当在DOS命令窗口中…

count/count if函数的基本用法

count函数&#xff0c;用来计算单元格的数的个数&#xff0c;只是用来计数&#xff0c;并且只有只记录数子的个数&#xff0c;文本的个数是不被记录的。 但是很少会用到单纯的count函数&#xff0c;往往在工作中计数是带有条件的。就会用到countif函数 COUNTIF函数需要注意的点…

EXCEL COUNTIF()的一些奇特的用法

文章目录 前言一、统计第几次重复二、统计不重复的数量三、通配符模糊统计四、防止重复录入五、忽略错误值或空值统计六、重复值填充背景色总结 前言 日常工作中需要度娘很多知识点或者方法&#xff0c;但每次用了就忘&#xff0c;下次遇到就需要继续度娘&#xff0c;故在此记…

Excel多条件计数——COUNTIFS【获奖情况统计】

问题描述 当前&#xff0c;我们需要对表格中的获奖情况进行统计 奖励级别&#xff1a;院级、校级、国家级、国际级奖励内容&#xff1a;特等奖、一等奖、二等奖、三等奖、优胜奖 功能要求 对所有奖励级别进行统计根据级别&#xff0c;计算内容数量 当有人的选项内容如下时 …

如何在Microsoft Excel中使用COUNTIF函数

COUNTIF 是一个 Excel 函数,用于对满足单个条件的区域中的单元格进行计数。COUNTIF可用于计算包含日期、数字和文本的单元格。COUNTIF 中使用的条件支持逻辑运算符(>、<、<>、=)和通配符(*、?)进行部分匹配。 例如,我们想计算包含 Google或 Facebook 的单元…

COUNT函数的使用

一、问题描述 今天在随手练习sql的时候&#xff0c;发现count查出来的数量和实际的数量不对&#xff0c;下面是我查询的sql 我想看看suitName字段一共有多少种数据 SELECTCOUNT(DISTINCT suitName) AS suitNameNum FROMrace_goods 得到的结果是suitName条数是22条&#xff0c…

countif是什么意思,如何运用?

countif是什么意思&#xff1f;countif函数是excel中对指定区域中符合指定条件的单元格计数的一个函数&#xff0c;简单来说就是算出某个参数的数量。那么&#xff0c;countif函数具体是如何运用的呢&#xff1f;小编分为两种情景为大家总结了操作步骤。 情景一&#xff1a;计算…

excel通过sumproduct和countifs不重复计数(数据中包含空白单元)

1. 常规情况&#xff0c;数据中不包含空白单元格&#xff0c;如下图&#xff1a; SUMPRODUCT((A2:A24E2)*(B2:B24F2)*(1/COUNTIFS(A2:A24,A2:A24,B2:B24,B2:B24,C2:C24,C2:C24))) 2. 数据中包含空白单元格&#xff0c;如下图&#xff1a; 数据中包含空白单元的情况&#xff0c…

countif怎么读(countif怎么读)

SUM,AVERAGE,MAX,MIN,PRODUCT,CUONT,RANK,IF,SUMIF,COUNTIF怎么读呀&#xff01;谢谢 SUM(桑母) 求和函数&#xff1b; AVERAGE(哎五瑞之) 求平均数函数&#xff1b; MAX(麦克斯) 最大值函数&#xff1b; MIN(民) 最小值函数&#xff1b; PRODUCT(普若达克特) 求积函数&#xf…

[Excel常用函数] countif countifs函数

countif函数 1.countif函数的含义 在指定区域中按指定条件对单元格进行计数&#xff08;单条件计数&#xff09; 2.countif函数的语法格式 countif&#xff08;range&#xff0c;criteria&#xff09; 参数range 表示条件区域——对单元格进行计数的区域。 参数criteria …

Microsoft Excel 教程:如何在 Excel 中使用 COUNTIF 函数?

欢迎观看 Microsoft Excel 教程&#xff0c;小编带大家学习 Microsoft Excel 的使用技巧&#xff0c;了解如何在 Excel 中使用 COUNTIF 函数。 COUNTIF 是一个统计函数&#xff0c;用于统计满足某个条件的单元格的数量&#xff1b;例如&#xff0c;某个姓名在单元格区域中出现…

[Excel函数] COUNT函数 | COUNTIF函数 | COUNTIFS函数

1.COUNT函数 语法: COUNT(value1,[value2],...) COUNT函数用于计算区域中包含数字的单元格的个数 (不统计文本性数字) COUNT函数很少单独使用&#xff0c;一般和其他函数嵌套使用 案例: 统计数字的个数 注意: A13该单元格数字为文本格式&#xff0c;COUNT函数不统计文本性数字 …

Excel如何使用COUNTIF函数

COUNTIF函数是我们在工作中经常使用的函数&#xff0c;今天就给大家分享一下如何使用countif函数。 1、COUNTIF函数主要用于对区域中满足单个指定条件的单元格进行计数。它的语法结构是COUNTIF(统计区域&#xff0c;统计条件) 注意要点 1.COUNTIF不区分大小写。 2.在条件中可…