线程池在java中有何用处_线程池知识点总结

线程池

线程池的优势

降低系统资源消耗,可以重用已存在的线程,减少了线程创建和销毁的不必要消耗;

提高系统响应速度,当有任务到达时,通过重用已存在线程,可以无需等待新线程的创建;

方便管理线程并发数量;

还有一些扩展功能,如延时定时线程池

线程池的流程

7229de37c1f4acf603600dd057afd3f2.png)

基础的使用

主要通过Executors的newXXXThreadPool();/newXXXThreadExecutor();方法来创建线程池(返回ExecutorService对象,ExecutorServcice是Java提供的用于管理线程池的类),通过对象的execute方法调用线程池里的线程执行对应的代码。

// FixedThreadPool(n)代表创建一个内部有固定n个的线程的线程池

ExecutorService executor = Executors.newFixedThreadPool(5);

// SingleThreadExecutor()代表创建一个只有一个线程的线程池

ExecutorService executor = Executors.newSingleThreadExecutor();

// CachedThreadPool()代表创建可以无限扩大的线程池

ExecutorService executor = Executors.newCachedThreadPool();

复制代码

newFixedThreadPool方法的源码:

0b85e29cb03ef195de096259d023ce94.png

newSingleThreadExecutor方法的源码:

987c4fa353614897382c8db0e2a2adac.png

newCachedThreadPool方法的源码:

3b73e66f111513aee3714b69798ce72a.png

使用的模板:

public void init(){

// 三种自带的线程池的创建方式

ExecutorService executor2 = Executors.newFixedThreadPool(5);

ExecutorService executor3 = Executors.newSingleThreadExecutor();

ExecutorService executor = Executors.newCachedThreadPool();

try {

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

// 用execute来使用线程池中的线程

executor.execute(() -> {

System.out.println(Thread.currentThread().getName() + " thread running");

});

}

} catch (Exception e) {

e.printStackTrace();

} finally {

// 最后需要关闭线程池

executor.shutdown();

}

}

复制代码

以上的三个方法最终都调用了下面的这个构造方法:

4e195c4671ee7721d791efbd0f5375eb.png

线程池中构造方法中的七大参数的用途

int corePoolSize :

表示线程池中常驻的核心线程数

int maximumPoolSize:

表示线程池中能容纳的同时执行的最大线程数,此值必须>=1

long keepAliveTime:

多余的空闲线程存活时间(线程池中线程数量超过corePoolSize,且空闲时间达到keepAliveTime,多余线程被销毁,到最后只剩下corePoolSize 的数量)

TimeUnit unit:

keepAliveTime的单位

BlockingQueue workQueue:

任务队列,存放被提交但是还未执行的任务

ThreadFactory threadFactory:

表示生产线程的线程工厂,一般默认即可

RejectedExecutionHandler handler:

定义拒绝策略,表示当队列满了,且工作线程数大于等于线程池的最大线程数以后如,何拒绝请求执行的runnable

为什么要使用阻塞队列,而不是非阻塞的?

阻塞队列在任务队列中没有任务时,阻塞获取任务的线程,使线程进入wait状态,释放CPU的资源。

当队列中有任务时才会唤醒对应的线程,从队列中取出消息并执行。

线程池底层工作的流程

线程池创建,等待请求

当调用execute()方法后,添加一个请求任务开始判断:

如果当前正在运行的线程数量小于corePoolSize,那么马上调用线程来执行这个任务;

如果当前正在运行的线程数量大于等于corePoolSize,且等待队列未满,那么将这个任务加入队列

如果此时队列已经满了,且正在运行的线程数量小于maximumPoolSize,那么扩容,即开启非核心线程来运行这个任务;

如果队列已满,且运行的线程数量大于等于maximumPoolSize,那么线程池回启动饱和拒绝策略来应对

当一个线程完成任务时,从等待队列中取出下一个任务来执行;

当一个线程空闲超过keepAliveTime,线程判断:

当运行的线程数大于corePoolSize,那么此时这个空闲线程被停止,否则不处理

最后线程池所有任务完成后,最终会收缩到corePoolSize的大小。

创建线程池的规范

根据阿里发布的开发手册:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这

样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2) CachedThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

所以在实际开发时一般会自己创建线程池:

public static void main(String[] args){

ExecutorService threadPool =

new ThreadPoolExecutor(2,// 常驻核心线程数

Runtime.getRuntime().availableProcessors() + 1,// 最大线程数

2,// 空闲存活的时间

TimeUnit.SECONDS,// 空闲存活时间的单位

new LinkedBlockingQueue<>(3),// 等待队列,一般需要给定队列的大小

Executors.defaultThreadFactory(),// 线程工厂,一般使用默认的

new ThreadPoolExecutor.DiscardOldestPolicy());// 拒绝策略

try {

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

threadPool.execute(() -> {

System.out.println(Thread.currentThread().getName() + " thread running... ...");

});

}

} catch (Exception e) {

e.printStackTrace();

} finally {

threadPool.shutdown();

}

}

复制代码

注意点:

线程池的最大的线程数(maximumPoolSize)一般可以设置为本机的 逻辑处理器数量+1 ,而逻辑处理器的数量可以通过Runtime.getRuntime().availableProcessors()来获取;

存放等待任务的阻塞队列最好赋予初始值,如果使用默认情况,队列的长度会过大,这样拒绝策略相当于无效了,不可取;

608d4642827dfa240f30df6e7f7dea8b.png

线程工厂通过Executors.defaultThreadFactory()来创建默认工厂;

拒绝策略通过new ThreadPoolExecutor.xxxPolicy()来创建指定

四种拒绝策略:

均发生在当线程全部在使用且阻塞队列满时

1、AbortPolicy()

丢弃要进入的任务,并且抛出RejectedExecutionException异常。

2、CallerRunsPolicy()

交给直接调用此线程的线程来处理要进入的任务。

3、DiscardPolicy()

丢弃要进入的任务,但是不抛出异常,静默丢弃。

4、DiscardOldestPolicy()

丢弃阻塞队列中排在最前面的任务,然后重新提交被拒绝任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值