java线程池详解

56 篇文章 5 订阅
35 篇文章 4 订阅

一、为什么需要线程池:

多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担。线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory。即便没有这样的情况,大量的线程回收也会给GC带来很大的压力。

为了避免重复的创建线程,线程池的出现可以让线程进行复用。通俗点讲,当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。

二、线程池的简介:

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行

线程池的主要特点为:线程复用、控制最大并发数、管理线程。

三、线程池的优势:

在这里插入图片描述
为何要用线程池 即 线程池的优势:
总体来说,线程池有如下的优势:

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

四、线程池的架构图:

interface:接口
在这里插入图片描述

在这里插入图片描述

两个接口:
1. 第一个接口是Executor它只定义了一个方法execute(Runnable command),相当于定义了一个框架,它能够执行一个任务。Executor就像是定义了一个框架,并确定了这个框架能够提供的功能。
在这里插入图片描述

2. 第二个接口是ExecutorService它继承Executor。主要扩展了关闭线程管理的一些功能,比如shutdown方法用来关闭线程池的任务,isTerminated方法来判断线程池的任务是否结束。

public interface ExecutorService extends Executor //继承了顶级接口Executor
在这里插入图片描述

另外ExecutorService还提供了最重要的方法submit,它支持了有返回结果的任务提交,也支持没返回值的任务提交,也引入了实现这个功能最关键的接口Callable、Future;

ExecutorService支持的比较广泛:
submit(Callable task):支持参数Callable,Callable是有返回值的接口
submit(Runnable task):支持参数Runnable ,Runnable是没有返回值的接口

Executor、ExecutorService都是接口(接口只定义方法,不实现方法),只是定义了提交任务、关闭任务等方法,相当于只是申明了线程池支持这些功能。

①:execute(Runnable command):执行行Ruannable类型的任务
②:submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
③:shutdown():温柔的关闭线程池,停止接受新任务,并执行完未完成的任务。
④:shutdownNow():强制关闭线程池,未完成的任务会以列表形式返回!
⑤:isTerminated():返回所有任务是否执行完毕。当调用shutdown()方法后,并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true;
⑥:isShutdown():返回线程池是否关闭,当调用shutdown()或shutdownNow()方法后返回为true。

3. AbstractExecutorService抽象类:

public abstract class AbstractExecutorService implements ExecutorService
在这里插入图片描述

AbstractExecutorService:它是抽象类并继承ExecutorService接口。它主要是实现了ExecutorService接口的submit系列方法

实现submit方法主要依靠RunnableFuture接口和它的实现类FutureTask。RunnableFuture继承Runnable和Future接口,而它的实现FutureTask的一个属性callable是Callable类型

FutureTask实现了Runnable的run方法,run方法调用的是callable的call方法保存执行结果。同时也实现Future的get方法获取结果,如果任务还没有执行则阻塞线程

所以submit方法实现的主要过程是提交的任务Callable封装成FutureTask,并把FutureTask当作Runnable丢给execute方法去异步执行,然后把FutureTask当作Future作为submit返回值

但是AbstractExecutorService并没有实现execute方法,所以它是一个抽象类,在等待有缘人来实现execute,实现线程池的最后一步。

4. 实现类ThreadPoolExecutor

ThreadPoolExecutor是Executor框架的正真实现者。它实现了execute方法,execute真正的实现,以下三个关键点:

HashSet workers;//Worker集合,线程池

BlockingQueue workQueue;//阻塞队列,要执行的任务

final void runWorker(Worker w);//Worker调用这个方法,可以从阻塞队列中获取任务来执行。

线程池工具类:Executors:
类似于集合和集合工具类
数组和数据的工具类
线程和线程的工具类
Executors中有多个用于创建线程池的方法;
在这里插入图片描述

五、Executors工具类中创建线程池的方法基本使用:

在这里插入图片描述

1.代码演示包含固定数量线程的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        try {
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//关闭线程池
        }
    }
}

在这里插入图片描述

2.代码演示包含一个线程的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池包含5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                /*threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });*/

                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /*threadPool.shutdown();*/  //关闭线程池
            threadPool2.shutdown();//关闭线程池2
        }
    }
}

在这里插入图片描述
3.代码演示包含N个线程 的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //一池包含N个线程,N的数量根据任务量大小动态变化
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                /*threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });*/

                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /*threadPool.shutdown();*/  //关闭线程池
            threadPool3.shutdown();//关闭线程池
        }
    }
}

在这里插入图片描述

或者这样:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //一池包含N个线程,N的数量根据任务量大小动态变化
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                /*threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });*/

                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 办理业务");
                });
                //MILLISECONDS:/ˈmɪlisekənd/毫秒
                try { TimeUnit.MILLISECONDS.sleep(200);}
                catch (InterruptedException e) {e.printStackTrace();}
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /*threadPool.shutdown();*/  //关闭线程池
            threadPool3.shutdown();//关闭线程池
        }
    }
}

在这里插入图片描述
创建线程池的方法解读:

本质上都是调用了ThreadPollExecutor(7个参数)方法:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

六、线程池的7个重要参数:

在这里插入图片描述

在这里插入图片描述

从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。

一、corePoolSize 线程池核心线程大小

核心线程数,也即最小的线程数/常驻线程数,线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

二、maximumPoolSize 线程池最大线程数量

一个任务被提交到线程池以后,首先会找有没有 空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中。

三、keepAliveTime 空闲线程存活时间:(多余/空闲线程数 = 最大线程数 - 核心线程数)

多余/空闲线程数 也可以叫非核心线程,核心线程+非核心线程=最大线程数

当线程池中的现存线程数量大于corePoolSize的时 候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime才会被销毁,最终会收缩到corePoolSize的大小。

四、unit 空闲线程存活时间单位

keepAliveTime的计量单位

五、workQueue 工作阻塞队列

用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
①:ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
②:LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
③:SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
④:priorityBlockingQuene:具有优先级的无界阻塞队列;

六、threadFactory线程工厂。它是ThreadFactory类型的变量,用来创建新线程。默认使用 Executors.defaultThreadFactory() 来创建线程。

七、handler拒绝策略。线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

  1. AbortPolicy - 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行(生产上一般不用,因为报异常)。

  2. CallerRunsPolicy - 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大

  3. DiscardPolicy - 丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。

  4. DiscardOldestPolicy - 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入

线程池监控API:

getActiveCount() :获取线程池中正在执行任务的线程数量
getCompletedTaskCount():获取已执行完毕的任务数
getTaskCount() :获取线程池已执行与未执行的任务总数
getPoolSize():获取线程池当前的线程数
getQueue().size():获取队列中的任务数

在这里插入图片描述

在这里插入图片描述

七、自定义一个线程池(自定义7大参数):

package com.fan.threadpool;
import java.util.concurrent.*;

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //自定义一个线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),//阻塞队列的容量,等候区
                Executors.defaultThreadFactory(),//使用工具类创建默认的线程工厂
                new ThreadPoolExecutor.AbortPolicy()//饱和拒绝策略
        );
        try {
            for (int i = 1; i <=7 ; i++) {
                final int temp = i;
                //execute相当于原先的run方法
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "\t 号窗口,服务顾客"+temp);
                    try { TimeUnit.SECONDS.sleep(3);}
                    catch (InterruptedException e) {e.printStackTrace();}
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//关闭线程池
        }
    }
}

当把for循环次数(即任务数)小于等于(核心线程数+阻塞队列容量)时,只创建核心线程处理任务:

在这里插入图片描述

当把for循环次数(即任务数)大于(核心线程数+阻塞队列容量)时,开始创建非核心线程处理任务:

在这里插入图片描述

当 任务数 大于 (线程规定的最大容量+阻塞队列容量) ,则触发饱和拒绝策略:

在这里插入图片描述

画图模拟线程池的处理过程:
核心线程:2
阻塞队列容量:3
最大线程数:5

过程:
在这里插入图片描述

在这里插入图片描述

八、线程池工作原理:

在这里插入图片描述

在这里插入图片描述

线程池工作院里的额底层:

  1. 在创建了线程池后,等待提交过来的任务请求。

  2. 当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

  • 如果 正在运行的线程数量 小于corePoolSize,那么马上创建线程运行这个任务;
  • 如果正在运行的线程数量 大于或等于corePoolSize,那么将这个任务放入队列
  • 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
  • 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
  1. 当一个线程完成任务时,它会从队列中取下一个任务来执行

  2. 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

  • 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
  • 所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。总结起来,也就是一句话,当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略

九、4种拒绝策略(handler):

当 任务数 大于 (线程规定的最大容量+阻塞队列容量) ,则触发饱和拒绝策略:

在这里插入图片描述

二、拒绝策略定义:
拒绝策略提供顶级接口 RejectedExecutionHandler ,其中方法 rejectedExecution 即定制具体的拒绝策略的执行逻辑。
jdk默认提供了四种拒绝策略:

  1. AbortPolicy - 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行(生产上一般不用,因为报异常,阻止了系统的正常运行)。

  2. CallerRunsPolicy - “调用者运行策略”是一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大

  3. DiscardPolicy - 直接丢弃任务,但是不抛出异常。如果允许任务丢失,这是最好的一种方案;可以配合这种模式进行自定义的处理方式。

  4. DiscardOldestPolicy - 丢弃队列中等待最久的任务,当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入

阿里开发手册规定不能使用 工具类Executors中的方式创建线程池:
在这里插入图片描述

在这里插入图片描述

代码演示四种拒绝策略的发生:

第一种AbortPolicy:

在这里插入图片描述
第二种CallerRunsPolicy:

在这里插入图片描述

main线程是调用者,多余任务被调用者main线程部分或者全部执行了:
在这里插入图片描述

在这里插入图片描述

第三种DiscardPolicy:直接丢弃:
在这里插入图片描述

第四种DiscardOldestPolicy:丢弃最老的:
在这里插入图片描述

总结:
四种拒绝策略是相互独立无关的,选择何种策略去执行,还得结合具体的业务场景。实际工作中,一般直接使用 ExecutorService 的时候,都是使用的默认的 defaultHandler ,也即 AbortPolicy 策略。

拒绝策略的选用:

第一种拒绝策略是 AbortPolicy,这种拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。

第二种拒绝策略是 DiscardPolicy,这种拒绝策略正如它的名字所描述的一样,当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。

第三种拒绝策略是 DiscardOldestPolicy,如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。

第四种拒绝策略是 CallerRunsPolicy,相对而言它就比较完善了,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。

第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。
第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。

十、合理设置线程池最大容量maximumPoolSize:

在这里插入图片描述

获得cpu核数:
System.out.println(Runtime.getRuntime().availableProcessors());

线程池核心线程数多少最为合适:分两种情况讨论:

CPU 密集型:
CPU密集型也叫计算密集型,cpu使用率较高(也就是一些复杂运算,逻辑处理)
比如说要计算1+2+3+…+ 1亿、计算圆周率后几十位、数据分析,视频高清解码等都是属于CPU密集型程序。
特点:占用cpu资源,线程切换 无开销

多核CPU处理CPU密集型程序才合适,而且中间可能没有线程的上下文切换(一个核心处理一个线程)

IO密集型:
IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,但CPU的使用率不高。

所以用脚本语言像python去做I/O密集型操作,效率就很快。

简单的说,就是需要大量的输入输出,比如读文件、写文件、传输文件、网络请求。比如接收一个前端请求–解析参数–查询数据库–返回给前端这样的,那么就是IO密集型的,例如web应用

区别和使用:
IO密集型:大量网络,文件操作
CPU 密集型:大量计算,cpu 占用越接近 100%, 耗费 多个核或多台机器

业务要具体分析,假如CPU现在是10%,数据量增大一点点,CPU狂飙,那也可能CPU密集型。

在这里插入图片描述

IO密集型的设置线程数的大小有三种种参考:
方法一:
由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数2
2
cpu核数:这种情况是假设cpu使用率达到了100%
在这里插入图片描述
方法二:
IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。
IO密集型时,大部分线程都阻塞,故需要多配置线程数:
参考公式:CPU核数 /(1 - 阻系数)
比如8核CPU:8/(1 - 0.9)=80个线程数
阻塞系数在0.8~0.9之间;

在这里插入图片描述

方式三:

在《Java并发编程实践》中,是这样来计算线程池的线程数目的:

一个基准负载下,使用 几种不同大小的线程池运行你的应用程序,并观察CPU利用率的水平。
给定下列定义:

Ncpu = CPU的数量
Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1
W/C = W/C:wait 等待时间/calculate 计算时间 比率

为保持处理器达到期望的使用率,最优的池的大小等于:

Nthreads = Ncpu x Ucpu x (1 + W/C)
即线程池数量=cpu核数 x cpu使用率 x (w/c+1)

有一个估算公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
W/C:wait 等待时间/calculate 计算时间

这个公式进一步转化为:生产中主要用这种方式

最佳线程数目 = (线程等待时间w与线程CPU时间之比 + 1)* CPU数目

以上可以得出一个结论:

线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

是否使用线程池就一定比使用单线程高效呢?

答案是否定的,比如Redis就是单线程的,但它却非常高效,基本操作都能达到十万量级/s。从线程这个角度来看,部分原因在于:

  1. 多线程带来线程上下文切换开销,单线程就没有这种开销
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池是一种用于管理和复用线程的机制,它可以提高多线程程序的性能和效率。在Java中,线程池由ThreadPoolExecutor类实现,通过设置不同的参数可以对线程池的行为进行调整。 以下是Java线程池的一些常用参数及其解释: 1. corePoolSize(核心线程数):线程池中始终保持的活动线程数,即使它们处于空闲状态。当有新任务提交时,如果活动线程数小于corePoolSize,则会创建新线程来处理任务。 2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数。当活动线程数达到maximumPoolSize并且工作队列已满时,新任务将会被拒绝。 3. keepAliveTime(线程空闲时间):当线程池中的线程数量超过corePoolSize时,多余的空闲线程在等待新任务到来时的最长等待时间。超过这个时间,空闲线程将被终止。 4. unit(时间单位):keepAliveTime的时间单位,可以是秒、毫秒、微秒等。 5. workQueue(工作队列):用于存储等待执行的任务的阻塞队列。常见的工作队列有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。 6. threadFactory(线程工厂):用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。 7. handler(拒绝策略):当线程池无法接受新任务时的处理策略。常见的拒绝策略有AbortPolicy(默认,抛出RejectedExecutionException异常)、CallerRunsPolicy(由调用线程执行任务)、DiscardPolicy(直接丢弃任务)和DiscardOldestPolicy(丢弃最旧的任务)。 这些参数可以根据实际需求进行调整,以达到最佳的线程池性能和资源利用率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值