【21】线程池原理

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

【21】线程池原理

1.线程池原理

1.1什么是线程池?为什么要用线程池?

(1)Thread线程,它是操作系统的一个资源,它是有资源消耗的。
(2)使用线程池,事先分配资源完成线程的创建,在执行任务的过程中,创建线程和消毁线程过程就不需要了统一由线程池完成。
(3)如果有一个任务就new一个实例出来,任务的执行时间。
T1:创建
T2:任务的执行时间
T3:线程的消毁时间

(4)线程池就是一上来就把线程的创建和消毁时间去掉,而是让任务直接执行,缩短任务的总的执行时间,即将线程的创建与消费时间交由线程池预先处理,以此节省时间。

(5)线程在操作系统中,是一种稀缺而昂贵的资源,会消耗CPU与内存,哪怕不做任何事情也会消耗内存。

(6)线程执行太多,也会对操作系统造成负担,会导致其他应用程序宕机,因此需要将线程统一的管理起来。

1.1.1线程池接口

(1)Executor

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

(2)ExecutorService

在这里插入图片描述

(3)ThreadPoolExecutor平时用的最多

在这里插入图片描述

在这里插入图片描述

1.2JDK中的线程池和工作机制

1.2.1线程池的创建
1.2.1.1各个参数含义
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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

(1)corePoolSize:当前线程池的核心线程数
(2)maximumPoolSize:当前线程池的最大线程数
(3)keepAliveTime与unit:如果当前线程池的线程数已经超过了corePoolSize,有些线程就空闲下来了,线程没事情做,就由这两个参数控制空闲线程的存活时间,如果空闲线程超过了存活时间,就会被消毁。
(4)workQueue:线程池是专门用来处理任务的,有可能产生往线程池中递交的任务数远远大于最大线程数,比如100,可能在很短的时间内递交了1000个任务进去,这种情况下就将另外900个任务放到阻塞队列中。
(5)handler:如果线程数实在是太多,线程池就可以拒绝,使用拒绝策略。
(6)threadFactory:一般给线程定义个名字

1.2.1.2线程池运行工作机制

在这里插入图片描述

(1)当我们定义了corePoolSize的时候,可以认为当前线程池中已启动,可以认为已经预先创建了几个线程,刚开始向线程池提交任务,其中的线程是很空闲的,就可以一个一个的做任务。

(2)当核心线程都被分配了执行任务,再往线程池里面添加任务,此时线程池并不会再创建新的线程去执行任务,而是将任务放到阻塞队列里面。

(3)继续提交任务到线程池,线程池的阻塞队列也被放满时,这时候线程池才会新启线程来执行任务。此时的线程数量一定是小于等于最大线程数的。

(4)如果最大线程数也满了,这时候我们的拒绝策略就会派上用场。这也是线程池的参数对线程工作机制的一个影响。

1.2.1.3线程池运行工作机制小结
  • 先在核心线程数范围之内承接任务
  • 超过核心线程数承接任务范围,则将任务放入阻塞队列,阻塞队列满了,再在最大线程数之内再启动线程承接任务。
  • 超过最大线程数承接任务范围,拒绝策略就开始发挥作用。
1.2.2线程池的拒绝策略

(1)实现类

在这里插入图片描述

(2)DiscardOldestPolicy:将最老的给消毁掉,执行当前任务。
(3)AbortPolicy:直接抛出异常
(4)CallerRunsPolicy:让调用者线程去执行任务,就是把任务让给往线程池里面添加任务的线程去做。
(5)DiscardPolicy:把最新提交的任务给扔掉。

(6)如果需要自定义拒绝策略,则可以实现RejectedExecutionHandler接口

1.2.3提交任务

线程池中提供了两种方法为线程池添加任务:

(1)java.util.concurrent.ThreadPoolExecutor#execute

public void execute(Runnable command) {

(2)java.util.concurrent.ExecutorService#submit(java.lang.Runnable, T)
当往线程池中添加任务需要返回结果的时候,使用submit方法实现。

<T> Future<T> submit(Runnable task, T result);

Future<?> submit(Runnable task);
1.2.4关闭线程池

提供了两个方法

(1)shutdown
尝试关闭线程池,将没有执行任务的线程进行中断。
java.util.concurrent.ThreadPoolExecutor#shutdown

(2)shutdownNow
无论线程池中的线程有没有在执行任务,全部中断。
java.util.concurrent.ThreadPoolExecutor#shutdownNow

去关闭正在执行任务的线程,它一定会成功吗?

  • 不一定。
  • 因为线程的中断是一种协作机制,需要看任务中如何处理中断信号的,需要看是否很好的在任务中处理任务的中断。

2.合理配置线程池

(1)主要是配置线程数量的大小与阻塞队列的大小

2.1任务特性

2.1.1CPU密集型

(1)CPU在不停的计算,频繁从内存中取数据进行计算。
(2)配置时,最大线程数量不要超过CPU核心数量。

  • CPU核心数的获取
public static void main(String[] args) {
        int cpuAvailabelNum = Runtime.getRuntime().availableProcessors();
        System.out.println(String.format("当前CPU可用的核心数为:%d",cpuAvailabelNum));
    }
  • maximumPoolSize:设置为机器CPU核心数+1
2.1.2IO密集型

(1)网络通信或磁盘读写频繁的任务,都称为IO密集型。

(2)网络与磁盘的读写速度要远远低于内存的读写速度的,执行任务的时候会导致线程一直处在磁盘或网络读写过程中,导致其他任务就得等待这个线程执行完成。所以此时的解决方法是,为线程多设置一些线程数。

(3)maximumPoolSize:设置为机器CPU核心数*2

2.1.3混合型

(1)任务既要频繁使用CPU又要频繁使用IO。

(2)如果任务的执行时间CPU处理与IO处理的时间相差不大,可以考虑拆分成两个线程处理,一个进行CPU处理,一个进行IO处理。

(3)如果时间相关非常大,这时候就没有必要考虑拆分线程来做了。

在这里插入图片描述

2.2配置

1)将BlockingQueue<Runnable> workQueue尽量配置成有界阻塞队列,不要配置成无界阻塞队列,因为很有可能撑爆机器,撑爆就是OOM。
U处理,一个进行IO处理。

3.打赏鼓励

感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!

3.1微信打赏

在这里插入图片描述

3.2支付宝打赏

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值