线程池知多少?

目录

前言

1.线程池的优缺点

2.JAVA 中线程池的继承关系图

3.核心参数与拒绝策略

4.线程池分类和适用场景

5.工作中的使用demo-1

6.工作中的使用demo-2

6.1定义线程池

6.2定义线程任务

6.3 统一的线程处理逻辑

6.4 线程任务的运行

7.线程池如何合理配置线程数?

7.1 CPU密集型

7.2 IO密集型:



前言

         线程池,顾名思义,存放线程的池子。线程是比较稀缺的资源,被无限创建的话,会大量消耗系统资源,降低系统的稳定性。JAVA 中的线程池,可以对线程进行统一的管理。

1.线程池的优缺点

 线程池解决的核心问题就是资源管理问题。使用线程池的优点:

  1. 降低资源损耗。通过复用已经创建的线程,来降低线程创建销毁带来的消耗。

  2. 提高响应速度。当接到任务时,减免了原本需要创建线程所消耗的时间。

  3. 提高了线程的可管理性。线程由线程池统一管理,调度。合理的使用线程池,能够避免线程的随意创建销毁,增加系统稳定性,控制线程数量,也可以避免资源耗尽的风险。

  4. 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行

在并发环境下,系统不能够确定在任意时刻中,执行多少任务,投入多少资源。这种不确定性可能会带来一些问题:

  1. 频繁的创建,删除线程,会带来一定的性能损耗。

  2. 线程无限创建,可能带来资源耗尽的风险。

  3. 线程随意的创建,删除。可能会导致系统的不稳定。

2.JAVA 中线程池的继承关系图

Executor 接口是线程框架最基础的部分,该接口只定义一个用于执行 Runnable 的 execute 方法,用来分离任务的提交和任务的执行。

ExecutorService 接口继承了 Executor,在其基础上做了 submit(), shutdown(), invokeAll() 等方法的扩展,算是真正意义上的线程池接口。

AbstractExecutorService 抽象类实现了 ExecutorService 中的部分方法,如部分的 submit(), invokeAll()方法。

ThreadPoolExecutor 是线程池的核心实现类,用来执行被提交的任务。

ScheduledExecutorService 接口继承了 ExecutorService 接口,提供了一些延时执行的方法。

ScheduledThreadPoolExecutor 是一个实现类,可以在给定一个延时后运行命令,或者定时执行命令。

图片

从继承图可以看出,ExecutorService 是线程池的一个比较核心的接口类。该类里面继承了和定义了一些具体的方法:

  1. void execute(Runnable command); 执行 Runnable 类型的任务。

  2. Future<?> submit(Runnable/Callable task); 用来提交 Runnable/Callable 任务,并返回该任务的 Future 对象。

  3. void shutdown(); 在完成已经提交的任务后,关闭。不再接收新的任务。

  4. List shutdownNow(); 停止所有正在执行的任务,同时不再接收新的任务。

  5. boolean isTerminated(); 判断是否所有的任务都已经执行完成了。

  6. boolean isShutdown(); 判断 ExecutorService 是否被关闭。

  7. List<Future> invokeAll(Collection<? extends Callable> tasks); 执行指定的任务集合,执行完成后返回结果。

  8. T invokeAny(Collection<? extends Callable> tasks); 执行指定的任务集合,任意一个任务执行完成,返回结果,其他任务终止。

3.核心参数与拒绝策略

核心参数:

  • corePoolSize:线程池中用来工作的核心的线程数量。

  • maximumPoolSize:最大线程数,线程池允许创建的最大线程数。

  • keepAliveTime:超出 corePoolSize 后创建的线程存活时间或者是所有线程最大存活时间,取决于配置。

  • unit:keepAliveTime 的时间单位。

  • workQueue:任务队列,是一个阻塞队列,当线程数已达到核心线程数,会将任务存储在阻塞队列中。

  • threadFactory :线程池内部创建线程所用的工厂。

  • handler:拒绝策略;当队列已满并且线程数量达到最大线程数量时,会调用该方法处理该任务。

提交优先级:从核心线程数   队列   最大线程数

执行优先级:从核心线程数     最大线程数  队列  

拒绝策略:

拒绝策略:就是当所请求的线程数量大于线程池的核心线程数的时候,先把线程任务放到任务队列里边,如果阻塞队列满后,还有线程任务直到线程池的最大线程数后,才会开启拒绝策略。

           第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满(默认的拒绝策略)

           第二种DisCardPolicy:不执行新任务,也不抛出异常

          第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行

           第四种CallerRunsPolicy:直接调用execute来执行当前任务

4.线程池分类和适用场景

newCachedThreadPool(可缓存)

  • 底层:返回ThreadPoolExecutor实例,corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)
  • 通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
  • 适用:执行很多短期异步的小程序或者负载较轻的服务器(可能造成cpu100%

newFixedThreadPool:(固定大小)

  • 底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue<Runnable>() 无界阻塞队列
  • 通俗:创建可容纳固定数量线程的池子,每个线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
  • 适用:执行长期的任务,性能好很多

newSingleThreadExecutor:(单个)

  • 底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue<Runnable>() 无解阻塞队列
  • 通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
  • 适用:一个任务一个任务执行的场景

NewScheduledThreadPool:(定时)

  • 底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列
  • 通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
  • 适用:周期性执行任务的场景

5.工作中的使用demo-1

在阿里巴巴手册中有一条建议:

【强制】线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

如果经常基于Executors提供的工厂方法创建线程池,很容易忽略线程池内部的实现。特别是拒绝策略,因使用Executors创建线程池时不会传入这个参数,直接采用默认值,所以常常被忽略。

@Service
public class ThreadExecutorService {

    /**
     * 等级规则调整等级线程池
     */
    ThreadPoolExecutor memberGradeThreadPoolExecutor   = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000));
    
    public void memberGradeTaskExecutor(Runnable runnable) {
        memberGradeThreadPoolExecutor.execute(runnable);
    }
    
}
@Slf4j
public class GradeTask implements Runnable {

//    @Resource
//    private UserClient            userClient;
//
//    @Resource
//    private MemberMapper          memberMapper;
    private String storeOrgId;

    private String modifiedId;

    private Integer modifyTime;

    public GradeTask(String storeOrgId, Integer modifyTime, String modifiedId) {
        this.storeOrgId = storeOrgId;
        this.modifiedId = modifiedId;
        this.modifyTime = modifyTime;
    }

    @Override
    public void run() {
        /**
         * 1.写你在多线程里面要执行的逻辑
         */
        System.out.println("线程开启;我就是来试试呀" + modifyTime);
        // MemberDo member = new MemberDo();
        //            BeanUtils.copyProperties(memberDo, member);
        //            BaseResponse<String> levelIdResponse = gradeClient.queryLevelByOrgId(storeOrgId, memberDo.getGrowthBalance());
        //            Assert.isTrue(ResultCode.Codes.SUCCESS.getCode().equals(levelIdResponse.getCode()), levelIdResponse.getMessage());
        //            memberDo.setLevelId(levelIdResponse.getData());
        //            memberDo.setModifyTime(new Date());
        //            memberMapper.updateByPrimaryKeySelective(memberDo);
        //            member.setNewLevelId(memberDo.getLevelId());
    }
}
@Autowired
    private ThreadExecutorService threadExecutorService;
    @Test
    void testThreadPool() {
        /**
         * 等级规则调整等级线程池
         */
        GradeTask gradeTask=new GradeTask("11",1,"111");
        GradeTask gradeTas2=new GradeTask("22",2,"222");
        GradeTask gradeTas3=new GradeTask("33",3,"333");
        GradeTask gradeTas4=new GradeTask("4",4,"444");
        List<GradeTask>list=new ArrayList<>();
        list.add(gradeTask);
        list.add(gradeTas2);
        list.add(gradeTas4);
        list.add(gradeTas3);
        for (GradeTask task : list) {
            threadExecutorService.memberGradeTaskExecutor(task);

        }
    }

6.工作中的使用demo-2

6.1定义线程池
package com.onlyqi.daydayupgo01.springSkill01.thread;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public interface IActionManager {
    int CORE_THREAD_SIZE = 5;
    int MAX_THREAD_SIZE = 10;
    AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
    String NAME_PREFIX = "AutoTaskPool-actionManager-";
    ThreadFactory SUB_THREAD_FACTORY = r -> new Thread(r, NAME_PREFIX + THREAD_NUMBER.getAndIncrement());

    ThreadPoolExecutor ACTION_EXECUTOR = new ThreadPoolExecutor(CORE_THREAD_SIZE, MAX_THREAD_SIZE, 30,
            TimeUnit.SECONDS, new LinkedBlockingDeque<>(), SUB_THREAD_FACTORY);


    /**
     * 执行线程
     * @param action
     */
    void action(Runnable action);
}

线程池接口的实现

package com.onlyqi.daydayupgo01.springSkill01.thread;

import org.springframework.stereotype.Component;

@Component("actionManager")
public class DefaultActionManager implements IActionManager {

    /**
     * 提交异步任务
     * @param action
     */
    @Override
    public void action(Runnable action){
        ACTION_EXECUTOR.execute(action);
    }
}
6.2定义线程任务
package com.onlyqi.daydayupgo01.springSkill01;

public class TestSpring01 implements Runnable {
    final private AutoTaskCtrl autoTaskCtrl;



    public TestSpring01(AutoTaskCtrl autoTaskCtrl) {
        this.autoTaskCtrl = autoTaskCtrl;
    }

    @Override
    public void run() {
        System.out.println("wohaoshuai=======我好帅============");
        autoTaskCtrl.autoTaskStart();
    }
}
package com.onlyqi.daydayupgo01.springSkill01;

public class TestSpring02 implements Runnable{
    final private AutoTaskCtrl autoTaskCtrl;

    public TestSpring02(AutoTaskCtrl autoTaskCtrl) {
        this.autoTaskCtrl = autoTaskCtrl;
    }

    @Override
    public void run() {
        autoTaskCtrl.autoTaskStop();
    }
}
6.3 统一的线程处理逻辑
package com.onlyqi.daydayupgo01.springSkill01;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class AutoTaskCtrl {

    private final static String PROCESS_KEYS_NOT = "320998";


    /**
     * 推送关闭自动派单消息
     * 判断当前时间在大于“系统配置【76612】的自动关闭派单时间”的5分钟之内,推送关闭自动派单的mc消息
     */
    public void autoTaskStart() {
        System.out.println("===========================1111====================");
        }


    public void autoTaskStop() {
        System.out.println("===========================222====================");
    }


}
6.4 线程任务的运行
@SpringBootTest
class DaydayupGo01ApplicationTests {
 @Autowired
    private IActionManager actionManager;
    @Autowired
    private AutoTaskCtrl autoTaskCtrl;


    @Test
    void contextLoadsOA() {
        actionManager.action(new TestSpring01(this.autoTaskCtrl));
        actionManager.action(new TestSpring02(this.autoTaskCtrl));
    }
}

运行结果 :

7.线程池如何合理配置线程数?

7.1 CPU密集型

定义:CPU密集型的意思就是该任务需要大量运算,而没有阻塞,CPU一直全速运行。

CPU密集型任务只有在真正的多核CPU上才可能得到加速(通过多线程)。

CPU密集型任务配置尽可能少的线程数。

CPU密集型线程数配置公式:(CPU核数+1)个线程的线程池

7.2 IO密集型:

定义:IO密集型,即该任务需要大量的IO,即大量的阻塞。

在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待。

所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要利用了被浪费掉的阻塞时间。

       第一种配置方式:

由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程。

配置公式:CPU核数 * 2。

      第二种配置方式:

IO密集型时,大部分线程都阻塞,故需要多配置线程数。

配置公式:CPU核数 / (1 – 阻塞系数)(0.8~0.9之间)

比如:8核 / (1 – 0.9) = 80个线程数

幸福就是:肉体无痛苦,灵魂无纷扰

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

only-qi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值