Java线程池

本文主要介绍java线程池相关的内容,包括线程池的几种常用方式

线程池存在的意义

先抛一个问题,Thread能直接新建并使用,为什么还要使用线程池?

如果看过我之前写的博客的话可以知道,一个线程从新建到死亡,总共有5个状态。创建、运行包括死亡都会消耗cpu等系统资源。如果子线程每次执行的任务量很小,但是数量很大时,你会发现,基本上所有的系统资源都消耗在线程管理上。同时线程之间的执行权竞争也会消耗一定的系统资源,这就导致了程序的执行效率降低。另外还有一点就是,随意新建的线程无法做到统一管理。线程池的存在就是为了解决上面提到的问题,以提高程序的执行效率。

Executor

Executor其实更准确的解释是执行器,定义了一个线程执行的规范,跟我们常说的线程池相差有点远。

An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.

这段话的意思是,Executor是用来执行被提交的Runnable任务的。这个接口提供了一种将任务提交与每个任务如何运行的机制解耦的方式,包括线程使用,调度等细节。通常Executor用来代替显示的创建线程。

上面是官方给的注释,那Executor这跟我们今天要说的线程有什么关系呢。

其实Executor是线程池的父类,最终的实现类有两个ThreadPoolExecutor以及ScheduledThreadPoolExecutor,其中ThreadPoolExecutor就是我们常说的线程池。

在ThreadPoolExecutor之上还有一个接口ExecutorService,定义了任务提交、执行等一些相关接口,继承结构图如下所示:

ExecutorService

相对于Executor,ExecutorService更多是功能的定义,提供了诸如

  1. Future submit(Callable task);
  2. Future<?> submit(Runnable task);

的方法,可以让线程执行返回结果。

ThreadPoolExecutor

这个才是我们常用的“线程池”,主要来看一下构造函数。

ThreadPoolExecutor有4个构造函数,

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 : 任务队列
  • threadFactory : 创建线程的工厂类
  • handler : 当任务无法被处理的对象
线程数量

关于corePoolSize、maximumPoolSize和workQueue这里要单独拿出来讲一下。

首先要明确两个概念:核心线程非核心线程

核心线程是指线程空闲时也保存的线程池中的线程,最大数量就是corePoolSize个,当然如果设置了allowCoreThreadTimeOut,那么核心线程也会被回收。

非核心线程是指核心线程不够处理任务,同时线程池稍微到达maximumPoolSize时添加的线程,这些线程会在空闲keepAliveTime后被回收。

但是保存在线程池中的线程并没有真正的核心线程和非核心线程的区别,只是开始回收线程时,线程数量达到核心线程的数量便不再回收。

知道上面这个两个我们就可以接着往下讲了。

什么时候新建核心线程

当线程池的线程数量少于corePoolSize时,不管是否有核心线程空闲,只要来新任务,都是直接新建核心线程用来出来任务。

什么时候新建非核心线程

这个问题比较复杂一点,因为涉及到workQueue的类型以及容量问题,直接一点的的回答就是:

  • 当workQueue被塞满时,这时来新的任务才会新建非核心线程进行处理,如果线程池中的线程数量达到了maximumPoolSize,这时再来新任务将被拒绝,同时抛出一个RejectedExecutionException。

  • 但是存在一个特殊的情况,那就是设置的核心线程数量为0,这个时候会新建一个非核心线程用于处理任务

什么时候将任务放入workQueue

当线程池中的核心线程达到了corePoolSize,同时没有核心线程空闲,这时来新的任务都会被放入workQueue,当workQueue被塞满时就会尝试新建非核心线程。

所以我们可以看到,一个线程池中线程池中能存在的最大线程个数就是maximumPoolSize,但是能保存的最大任务数就会比这个大一点,是maximumPoolSize + workQueue.size() 之和。

大致的流程下图所示:

下面是demo验证一下正常情况:

public class ExecutorTest {

    public static void main(String[] args) {

try {

            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

            for (int i = 0; i < 10; i++) {

                final int index = i;

                executor.execute(() -> {
                    try {
                        Thread.sleep(200);
                        System.out.println("runnable index is " + index
                                + " and time is " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }


            //workers 在线程池中用保存有新建完的线程,由于不存在直接获取的方法,这里用反射获取
            Class clazz = executor.getClass();
            Field workers = clazz.getDeclaredField("workers");
            workers.setAccessible(true);

            while (true) {

                HashSet hashSet = (HashSet) workers.get(executor);
                System.out.println("Queue size is " + executor.getQueue().size()
                        + " and thread count is " + hashSet.size());


                Thread.sleep(1000);
            }
        } catch (Exception ignore) {

        }
    }
}
复制代码

打印如下:

time is 1525846211057 and thread count is 1
runnable index is 0 and time is 1525846211260
runnable index is 1 and time is 1525846211464
runnable index is 2 and time is 1525846211665
runnable index is 3 and time is 1525846211870
time is 1525846212062 and thread count is 1
runnable index is 4 and time is 1525846212074
time is 1525846213064 and thread count is 1
time is 1525846214069 and thread count is 1
time is 1525846215070 and thread count is 1
time is 1525846216074 and thread count is 1
time is 1525846217076 and thread count is 1
time is 1525846218079 and thread count is 1
time is 1525846219084 and thread count is 1
time is 1525846220089 and thread count is 1
time is 1525846221092 and thread count is 1
复制代码

由于没有提供直接获取线程池中当前线程数量方法,用反射的方法来获取。

可以看到,就算非核心线程数量设置到了Integer.MAX_VALUE,但是任务队列并没有满,还是只有1个核心线程在执行任务。并且过了超时时间,线程并没有被销毁。

再来一个demo验证一下核心线程设置为0的情况:

public class ExecutorTest {

    public static void main(String[] args) {

try {

            ThreadPoolExecutor executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

            for (int i = 0; i < 10; i++) {

                final int index = i;

                executor.execute(() -> {
                    try {
                        Thread.sleep(200);
                        System.out.println("runnable index is " + index
                                + " and time is " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }


            //workers 在线程池中用保存有新建完的线程,由于不存在直接获取的方法,这里用反射获取
            Class clazz = executor.getClass();
            Field workers = clazz.getDeclaredField("workers");
            workers.setAccessible(true);

            while (true) {

                HashSet hashSet = (HashSet) workers.get(executor);
                System.out.println("Queue size is " + executor.getQueue().size()
                        + " and thread count is " + hashSet.size());


                Thread.sleep(1000);
            }
        } catch (Exception ignore) {

        }
    }
}
复制代码

打印如下:

time is 1525846316180 and thread count is 1
runnable index is 0 and time is 1525846316383
runnable index is 1 and time is 1525846316589
runnable index is 2 and time is 1525846316793
runnable index is 3 and time is 1525846316999
time is 1525846317184 and thread count is 1
runnable index is 4 and time is 1525846317201
time is 1525846318188 and thread count is 1
time is 1525846319193 and thread count is 1
time is 1525846320197 and thread count is 1
time is 1525846321198 and thread count is 1
time is 1525846322202 and thread count is 1
time is 1525846323206 and thread count is 0
time is 1525846324206 and thread count is 0
time is 1525846325211 and thread count is 0
复制代码

当核心线程设置为0时,提交任务给线程池,会新建一个非核心线程用于处理任务,过了超时时间后线程就被回收了。

Executors提供的4种默认线程池

Executors是一个线程池的工具类,最主要的用处就是给我们提供了4种常用的线程池的创建方式。

newFixedThreadPool(int nThreads)

这个静态返回一个固定线程数量的线程池,来看一下里面的实现。

public static ExecutorService newFixedThreadPool(int nThreads) {          
    return new ThreadPoolExecutor(nThreads, nThreads,                     
                                  0L, TimeUnit.MILLISECONDS,              
                                  new LinkedBlockingQueue<Runnable>());   
}                                                                         
复制代码

这里就开始用到文章上面所解释的参数的意义,这里可以看到,核心线程和最大线程数量都为指定的nThreads,非核心线程超市时间为0,也就是默认值,任务队列为一个LinkedBlockingQueue。

解释一下为什么这是一个固定线程的线程池,结合上面的任务分配图可以很清楚的知道,

  1. 当新任务来时,最开始肯定是新建核心线程进行任务直接处理
  2. 当核心线程满了之后就是任务入队了,但是LinkedBlockingQueue理论上是一个无限大的队列,可以一直存放任务,这个线程池中不会存在非核心线程,线程的数量也就固定了。
newSingleThreadExecutor()

这个就是新建一个只有一个核心线程的线程池。

public static ExecutorService newSingleThreadExecutor() {              
    return new FinalizableDelegatedExecutorService                     
        (new ThreadPoolExecutor(1, 1,                                  
                                0L, TimeUnit.MILLISECONDS,             
                                new LinkedBlockingQueue<Runnable>())); 
}                                                                      
复制代码

这个就不多解释了,看上面就知道了,只是把核心线程数指定为了1。

newCachedThreadPool()

这就是不限制线程的数量,但是线程会被回收。

public static ExecutorService newCachedThreadPool() {                
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,              
                                  60L, TimeUnit.SECONDS,             
                                  new SynchronousQueue<Runnable>()); 
}                                                                    
复制代码

还是套用上面的任务分配图:

  1. 核心线程数量为0,以为这所有的任务都先进任务队列
  2. 但是SynchronousQueue是一个同步的队列,也就是SynchronousQueue中不会存在任务,只要有新任务都是直接分配出去处理。
  3. 最大线程数量为Integer.MAX_VALUE,意味着可以处理的任务是无限量的。
  4. 看完上面几点可以知道,当有任务来时都是交给非核心线程处理的,线程数量不够时直接新建非核心线程。所有线程空闲超过60秒将会被回收,这也是Cached的含义。
newScheduledThreadPool(int corePoolSize)

这个线程池主要用于执行需要重复执行的任务上,可以指定任务循环执行的模式。

public static ScheduledExecutorService newScheduledThreadPool(          
        int corePoolSize, ThreadFactory threadFactory) {                
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}                                                                       
复制代码
public ScheduledThreadPoolExecutor(int corePoolSize) {                  
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,              
          new DelayedWorkQueue());                                      
}                                                                       
复制代码

可以看到最后其实是新建了一个ThreadPoolExecutor,设置的核心线程数为corePoolSize,队列为一个无限大小的延时队列。

三个方法说明一下:

  1. schedule(Runnable command,long delay,TimeUnit unit):就是一个只执行一次的延时任务,这个方法接受3个参数

    • 执行的任务
    • 延时的时间
    • 时间单位
  2. scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit): 这是一个无限次执行的延时任务,这个方法接受4个参数

    • 执行的任务
    • 第一次开始执行的延时
    • 以后每次开始的延时
    • 时间单位
  3. scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit):这是一个无限次执行的延时任务,只不过每次新任务总在上一个任务执行完毕开始,这方法接受4个参数

    • 执行的任务
    • 第一次开始执行的延时
    • 上个任务结束后,开始下个任务的延时
    • 时间单位

总结

这篇基本上算都是干货了,总结没啥好写的,再看一遍文章,能记住的都记住吧,特别是文中提到的几个问题。

转载于:https://juejin.im/post/5b9486fce51d450e4369104d

<think>我们正在讨论Java线程池的使用和原理。根据引用内容,线程池Java中主要通过ThreadPoolExecutor类实现,从JDK5开始内置支持。线程池的好处包括重用线程、控制并发数量、管理线程生命周期等。常见的线程池类型有:FixedThreadPool(固定容量)、CachedThreadPool(缓存)、SingleThreadExecutor(单线程)、ScheduledThreadPool(定时任务)以及通过ThreadPoolExecutor自定义创建。我们将分步骤讲解:1.线程池核心原理2.创建线程池的五种方式(包括不推荐的原因)3.重点讲解ThreadPoolExecutor及其参数4.线程池的工作机制(任务提交、执行、拒绝策略等)5.使用示例注意:根据引用[3]和[4],ThreadPoolExecutor是构建线程池核心类,因此我们将重点分析。步骤1:线程池核心原理线程池通过预先创建一定数量的线程并维护它们,避免频繁创建和销毁线程的开销。当有任务提交时,线程池分配空闲线程执行任务;若没有空闲线程,任务将被放入队列等待;当队列满时,根据设定的拒绝策略处理。步骤2:创建线程池的五种方式(引用[3]和[5])1.newCachedThreadPool:创建一个可缓存的线程池线程数可无限增加(最大为Integer.MAX_VALUE),空闲线程默认60秒回收。不推荐原因:可能创建过多线程导致OOM。2.newFixedThreadPool:固定大小的线程池,使用无界队列(LinkedBlockingQueue)。不推荐原因:无界队列可能堆积大量任务导致OOM。3.newSingleThreadExecutor:只有一个线程线程池,同样使用无界队列。不推荐原因同上。4.newScheduledThreadPool:用于定时或周期性任务的线程池。不推荐直接使用的原因是其最大线程数为Integer.MAX_VALUE,可能创建过多线程。5.推荐方式:直接使用ThreadPoolExecutor构造函数创建,可明确指定参数,避免资源耗尽。步骤3:ThreadPoolExecutor的七个参数(引用[3]和[4])构造方法如下:```javapublicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler)```1.corePoolSize:核心线程数,即使空闲也不会被回收(除非设置allowCoreThreadTimeOut)2.maximumPoolSize:最大线程数3.keepAliveTime:非核心线程的空闲存活时间4.unit:存活时间单位5.workQueue:任务队列(常用有ArrayBlockingQueue有界队列,LinkedBlockingQueue无界队列,SynchronousQueue同步移交队列)6.threadFactory:线程工厂,用于创建线程7.handler:拒绝策略(当线程池队列都满时触发)步骤4:线程池的工作机制(任务提交执行流程)1.提交任务后,如果当前线程数小于corePoolSize,则创建新线程执行(即使有空闲线程)。2.如果当前线程数>=corePoolSize,则将任务放入workQueue。3.如果队列已满,且线程数小于maximumPoolSize,则创建新的非核心线程执行。4.如果队列已满且线程数达到maximumPoolSize,则触发拒绝策略。拒绝策略(handler)有四种:-AbortPolicy(默认):抛出RejectedExecutionException-CallerRunsPolicy:由调用线程执行该任务-DiscardPolicy:丢弃任务,不抛异常-DiscardOldestPolicy:丢弃队列最前面的任务,然后重试步骤5:使用示例(以ThreadPoolExecutor为例)下面是一个自定义线程池的示例:```javaimportjava.util.concurrent.*;publicclassThreadPoolExample{publicstaticvoidmain(String[]args){//创建线程池ThreadPoolExecutorexecutor=newThreadPoolExecutor(2,//核心线程数5,//最大线程数60,//空闲线程存活时间TimeUnit.SECONDS,newArrayBlockingQueue<>(10),//有界队列,容量10Executors.defaultThreadFactory(),newThreadPoolExecutor.AbortPolicy());//拒绝策略:直接抛出异常//提交任务for(inti=0;i<15;i++){finalinttaskId=i;executor.execute(()->{System.out.println("Task"+taskId+"isrunningonthread"+Thread.currentThread().getName());try{Thread.sleep(1000);//模拟任务执行}catch(InterruptedExceptione){e.printStackTrace();}});}//关闭线程池executor.shutdown();}}```注意:实际任务数15个,核心线程2个,最大线程5个,队列容量10个。因此前2个任务会创建核心线程执行,接下来的10个任务放入队列,然后第13、14、15个任务会创建非核心线程(共3个非核心线程,总线程数5个)。如果超过15个任务(比如16个),则会触发拒绝策略。最后,关于线程池的关闭,可以使用shutdown()(等待已提交任务完成)或shutdownNow()(尝试中断所有线程,停止任务)。总结:使用线程池时,推荐直接使用ThreadPoolExecutor构造函数,根据实际需求设置参数,避免使用Executors工厂方法可能导致的资源耗尽问题。相关问题:1.线程池中任务队列有哪些类型?各自特点是什么?2.如何合理设置线程池核心线程数和最大线程数?3.线程池的拒绝策略有哪些?如何自定义拒绝策略?4.如何监控线程池的运行状态?5.线程池线程是如何创建的?如何自定义线程工厂?</think>### Java线程池使用指南与原理分析 Java线程池通过`java.util.concurrent`包实现,核心类是`ThreadPoolExecutor`。线程池核心思想是**复用线程**,避免频繁创建销毁线程的开销。线程池中的线程执行完任务后不会销毁,而是回到池中等待新任务[^2]。 --- #### **一、线程池的创建方式** Java提供5种创建线程池的方法(实际都是`ThreadPoolExecutor`的包装): 1. **固定大小线程池**(推荐) ```java ExecutorService fixedPool = Executors.newFixedThreadPool(5); // 固定5个线程 ``` - 特点:核心线程数=最大线程数,使用无界队列`LinkedBlockingQueue` - 风险:任务过多时可能导致OOM(队列无限增长)[^5] 2. **缓存线程池**(不推荐) ```java ExecutorService cachedPool = Executors.newCachedThreadPool(); ``` - 特点:线程数无上限(`Integer.MAX_VALUE`),空闲线程60秒回收 - 风险:高并发时可能创建大量线程导致OOM[^3] 3. **单线程池** ```java ExecutorService singlePool = Executors.newSingleThreadExecutor(); ``` - 特点:仅1个线程,保证任务顺序执行 - 风险:同固定线程池(无界队列问题) 4. **定时任务线程池** ```java ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3); ``` 5. **推荐做法:手动创建`ThreadPoolExecutor`** ```java ThreadPoolExecutor customPool = new ThreadPoolExecutor( 4, // 核心线程数 10, // 最大线程数 60, // 空闲线程存活时间(秒) TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), // 有界队列(容量100) new ThreadPoolExecutor.AbortPolicy() // 拒绝策略 ); ``` --- #### **二、核心参数详解(7个关键参数)** ```java public ThreadPoolExecutor( int corePoolSize, // 核心线程数(常驻线程) int maximumPoolSize, // 最大线程数 long keepAliveTime, // 非核心线程空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 ) ``` 1. **任务队列类型** - `ArrayBlockingQueue`:**有界队列**(推荐),避免OOM - `LinkedBlockingQueue`:**无界队列**(默认) - `SynchronousQueue`:不存储任务,直接移交线程 2. **拒绝策略**(当队列线程池全满时触发) - `AbortPolicy`(默认):抛出`RejectedExecutionException` - `CallerRunsPolicy`:由调用者线程直接执行任务 - `DiscardPolicy`:静默丢弃任务 - `DiscardOldestPolicy`:丢弃队列最旧的任务 --- #### **三、线程池工作原理** 1. **任务提交流程**: - 当线程数 < `corePoolSize`,**创建新线程**执行任务 - 当线程数 >= `corePoolSize`,任务进入**工作队列** - 当队列已满且线程数 < `maximumPoolSize`,**创建临时线程**执行 - 当队列满且线程数达到`maximumPoolSize`,触发**拒绝策略**[^4] 2. **线程回收机制**: - 核心线程默认不回收(除非设置`allowCoreThreadTimeOut(true)`) - 非核心线程空闲超过`keepAliveTime`后自动销毁 --- #### **四、最佳实践** 1. **参数设置建议**: - CPU密集型任务:核心线程数 = CPU核数 + 1 - IO密集型任务:核心线程数 = 2 * CPU核数 - 使用**有界队列**防止资源耗尽 - 拒绝策略选择`CallerRunsPolicy`保证任务不丢失 2. **代码示例**: ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), // 核心线程数=CPU核数 Runtime.getRuntime().availableProcessors() * 2, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(500), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); // 提交任务 executor.execute(() -> System.out.println("Task running")); // 优雅关闭 executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); ``` 3. **注意事项**: - 避免使用`Executors`快捷方法(有OOM风险) - 监控线程池状态(通过`getActiveCount()`等方法) - 务必调用`shutdown()`释放资源 --- #### **五、原理解析** - **线程复用**:通过`Worker`类封装线程,循环从队列获取任务执行[^3] - **任务调度**:核心逻辑在`ThreadPoolExecutor.execute()`方法中 - **资源控制**:通过`BlockingQueue`实现生产者-消费者模型 > 线程池的本质是**资源池化技术**(Pooling),通过复用线程降低系统开销,同时提供流量缓冲和容错能力[^4]。 --- ### 相关问题 1. **为什么`FixedThreadPool`使用无界队列会导致OOM?如何避免?** 2. **线程池中`corePoolSize`和`maximumPoolSize`设置不当会产生什么后果?** 3. **如何实现一个自定义拒绝策略(如记录日志或降级处理)?** 4. **线程池的`shutdown()`和`shutdownNow()`有什么区别?** 5. **如何监控线程池的运行状态(如队列积压、活跃线程数)?** [^1]: ThreadPoolExecutor是Java线程池核心实现类。 [^2]: JDK5开始内置线程池线程执行后返回池中复用。 [^3]: 手动创建ThreadPoolExecutor可避免资源耗尽风险。 [^4]: 线程池通过队列缓冲和动态扩缩容管理任务。 [^5]: FixedThreadPool的无界队列可能导致OOM。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值