深入理解线程池

概述:

在这里插入图片描述

随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力。

所以,多线程技术是服务端开发人员必须掌握的技术。线程的创建和销毁,都涉及到系统调用,比较消耗系统资源,所以就引入了线程池技术,避免频繁的线程创建和销毁。

在Java用有一个Executors工具类,可以为我们创建一个线程池,其本质就是new了一个ThreadPoolExecutor对象。

线程池的优势:

总体来说,线程池有如下的优势:
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

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

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。避免由于资源不足而发生宕机等问题;

同样思想的设计还有很多,比如数据库连接池,由于频繁的连接数据库,然而创建连接是一个很消耗性能的事情,所以数据库连接池就出现了。

线程池入门代码编写:

/**
    目标:创建一个线程池。

    线程池在Java中的代表类:ExecutorService(接口)。

    Java在Executors类下提供了一个静态方法得到一个线程池的对象:
         1.public static ExecutorService newFixedThreadPool(int nThreads):
            创建一个线程池返回。

    ExecutorService提交线程任务对象执行的方法:
         1.Future<?> submit(Runnable task):提交一个Runnable的任务对象给线程池执行。
    小结:
         pools.shutdown(); // 等待任务执行完毕以后才会关闭线程池
         pools.shutdownNow(); // 立即关闭线程池的代码,无论任务是否执行完毕!
         线程池中的线程可以被复用,线程用完以后可以继续去执行其他任务。
 */
public class ThreadPoolsDemo02 {
    
    
    public static void main(String[] args) {
    
    
        // 1、创建一个固定核心线程数量的线程池!
        ExecutorService pools = Executors.newFixedThreadPool(3);
        // 2、提交线程任务对象给线程池处理
        Runnable target = new MyRunnable();
        pools.submit(target);  // 线程池自动创建线程对象,并自动触发任务执行!
        pools.submit(target);  // 线程池自动创建线程对象,并自动触发任务执行!
        pools.submit(target);  // 线程池自动创建线程对象,并自动触发任务执行!
        pools.submit(target);  // 这里不会再创建线程,会复用之前的线程!
        pools.shutdown(); // 等待任务执行完毕以后才会关闭线程池
        //pools.shutdownNow(); // 立即关闭线程池的代码,无论任务是否执行完毕!
    }
}

线程池的创建:

在这里插入图片描述
在这里插入图片描述
corePoolSize:线程池的核心线程数,说白了就是,即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。

maximumPoolSize:最大线程数,不管你提交多少任务,线程池里最多工作线程数就是maximumPoolSize。

keepAliveTime:线程的存活时间。当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则线程退出。

unit:这个用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS。

workQueue:一个阻塞队列,提交的任务将会被放到这个队列里。

threadFactory:线程工厂,用来创建线程,主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。

handler:拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用。

线程池执行流程

这里用一个图来说明线程池的执行流程

在这里插入图片描述
1.如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;

2.如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

3.如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

1、先看一下线程池的executor方法

在这里插入图片描述
1.判断当前活跃线程数是否小于corePoolSize,如果小于,则调用addWorker创建线程执行任务
如果不小于corePoolSize,则将任务添加到workQueue队列。

2.如果放入workQueue失败,则创建线程执行任务,

3.如果这时创建线程失败(当前线程数不小于maximumPoolSize时),就会调用reject(内部调用handler)拒绝接受任务。

2、再看下addWorker的方法实现

在这里插入图片描述
这块代码是在创建非核心线程时,即core等于false。判断当前线程数是否大于等于maximumPoolSize,如果大于等于则返回false,即上边说到的3中创建线程失败的情况。

addWorker的下半部分:

在这里插入图片描述

1.创建Worker对象,同时也会实例化一个Thread对象。
2.启动启动这个线程

3、再到Worker里看看其实现

在这里插入图片描述
可以看到在创建Worker时会调用threadFactory来创建一个线程。上边的2中启动一个线程就会触发Worker的run方法被线程调用。

4、接下来看看runWorker方法的逻辑

在这里插入图片描述
线程调用runWoker,会while循环调用getTask方法从workerQueue里读取任务,然后执行任务。只要getTask方法不返回null,此线程就不会退出。

5、最后在看看getTask方法实现

在这里插入图片描述

先不管allowCoreThreadTimeOut,这个变量默认值是false。wc>corePoolSize则是判断当前线程数是否大于corePoolSize。如果当前线程数大于corePoolSize,则会调用workQueue的poll方法获取任务,超时时间是keepAliveTime。如果超过keepAliveTime时长,poll返回了null,上边提到的while循序就会退出,线程也就执行完了。

创建多少线程比较合适?

我们从线程的应用场景来分析, 由于IO操作比Cpu计算耗时要久的多的,如果我们一段程序有IO操作 和 Cpu计算 我们可以称之为:IO密集型计算。程序中没有IO操作 只有Cpu的话 称之为Cpu密集型程序。

IO密集计算:

如何将硬件利用率达到极致呢 我们将 R = IO耗时 / Cpu耗时 , 如果IO耗时/Cpu耗时 = 10 (我们平常可以用工具apm来统计这个比例) 创建线程A 执行io操作 我们希望IO操作的时候 Cpu不能闲着 所以就应该创建10个线程去执行Cpu计算 当Io操作完毕后刚好Cpu也执行完毕 ,他们的利用率都是百分之100 在执行这段代码的时候。

这个例子我们要创建 1+ 10 = 11个线程执行起来效率更高,于是我们就得到了公式: 1+ I/O耗时/Cpu耗时,如果是多核Cpu 最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / Cpu 耗时)

Cpu密集型

:这个就很简单了 Cpu的核数 = 线程数就行,一般我们会设置 Cpu核数+1 防止由于其他因素导致线程阻塞等。

几种常见的线程池:

1.FixedThreadPool:

适合场景:适合少量的大任务(大任务处理慢,如果线程数量多的话,反而在切换线程上下文时损耗,所以控制线程在一定的数量)。

2.CachedThreadPool:

适合场景:大量的小任务(每个任务处理快,不会频繁出现线程处理一半时,切换其他线程)。

3.SecudleThreadPool:

周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。

4.SingleThreadPool:

只有一条线程来执行任务,适用于有顺序的任务的应用场景。

猜你喜欢

转载自blog.csdn.net/weixin_46011971/article/details/107533998