• 关于线程池


            线程池是一种多线程处理方式,通过将任务添加到队列中,在创建线程后自动启动这些任务。线程池中的线程可以复用,可以控制最大并发数,并且方便管理,也就是说,线程池会提前创建好一些线程,当后续需要用到线程时,从线程池里拿就行,一旦任务结束,线程并不会结束,而是回归到线程池,等待下一个任务,然后继续执行。那么为什么不什么时候使用线程就什么时候创建线程呢?这样不比往线程池里面拿线程更香吗?这是错误的,使用线程池显然更香,因为使用线程池比从系统里面创建新的线程更快更高效。为什么呢?因为如果是从系统这里直接创建线程,需要调用系统api,进一步的由操作系统内核,完成线程的创建过程,但是,由于内核是为所有的线程服务的,什么时候创建线程由系统内核决定,如果在系统内核繁忙时创建,则需要等待一定的时间使得内核有空闲时在创建,因此它是不可控的,而如果是从线程池这里获取线程,上述的内核中进行的操作,都是提前做好的,就算系统繁忙,也可以直接获取线程,不需要等待,因为它是直接创建好的,现在取线程的过程,纯粹的用户代码完成(纯用户态),它则是可控的。线程池有三种创建方式:单一线程池、固定大小线程池和可伸缩线程池。单一线程池只有一个线程,固定大小线程池指定了线程池的大小,而可伸缩线程池可以根据需要动态改变线程池的大小。线程池的核心组成是线程和任务,线程是前面学过的线程,任务是实现了Runnable或Callable接口的实例对象。线程池的代码实现可以使用Executor框架中的Executors类来创建,也可以使用ThreadPoolExecutor类来自定义线程池

    ExecutorService service = Executors.newFixedThreadPool(4);//ExecutorService service是线程池的对象,通过Executors里的方法来创建,创建了有4个线程的线程池。
    ExecutorService service1 = Executors.newCachedThreadPool();//创建线程动态变化的线程池,已经创建了的线程不会回收。
    ExecutorService service2 = Executors.newSingleThreadExecutor();//创建只有单个线程的线程池,与直接创建线程相比,更为方便。

    线程池通过submit来添加任务,参数为Runnable对象,

    service.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("haha");
        }
    });

    除此之外,标准库还提供了一个接口更为丰富的线程池类:ThreadPoolExecutor,上述的线程池创建方法,其实是对ThreadPoolExecutor的各个不同的创建方法而进行的封装。

            ThreadPoolExecutor有多个构造方法,但只需要掌握这一个,就可以掌握所有的构造方法,这一个构造方法就是

    public ThreadPoolExecutor(int corePoolSize,//核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//线程最大空闲时间
                              TimeUnit unit,//时间单位
                              BlockingQueue workQueue,//管理任务的阻塞队列
                              ThreadFactory threadFactory,//ThreadFactory threadFactory是线程工厂,可以通过它来创建线程
                              RejectedExecutionHandler handler)//拒绝方式

    首先得知道,ThreadPoolExecutor类的线程池里的线程个数并不是一成不变的,而是会根据当前任务的情况动态发生变化,但这样的变化也是有限制的,corePoolSize和maximumPoolSize就是对里面线程数目的限制,其中corePoolSize表示核心线程数,也就是最少线程数,而maximumPoolSize则表示最大线程数,任你任务再多,创建的线程数目也不能超过这个数目。当空闲的时候,又会回收掉创建的线程,但线程数目不能少于核心线程数,这样就能保证繁忙时能高效的处理问题,空闲时也不会浪费资源。long keepAliveTime和TimeUnit unit则表示允许线程空闲的时间,其中keepAlive是时间数字,如3000,而unit则是单位,如(秒,毫秒之类的),当线程空闲时间超过这个时间后,则会对该线程进行回收,避免浪费资源。BlockingQueue workQueue则是要执行的任务,线程池中有很多任务,它们可以使用阻塞队列来进行管理,这个阻塞队列既可以是线程池内置的,也可以是手动指定的,如根据优先级来指定一个优先级队列。ThreadFactory threadFactory是线程工厂,可以通过它来创建线程,可以通过不同ThreadFactory来来创建不同的线程。RejectedExecutionHandler handler是这里的重点,它的含义是拒绝方式或者拒绝策略。当阻塞队列满了后,继续添加任务,该如何应对,你或许会问:让它在这里等待不就好了吗?这样的方法可行,但并不适合所有情况,因此,得需要一个确切的方式,以下就是拒绝方式。

    1)ThreadPoolExecutor.AbortPolicy:当阻塞队列满了之后,如果继续添加,那么就会抛出异常,线程池直接不干了,不仅先前的任务不干了,新添加的任务也不干了。

    2)ThreadPoolExecutor.CallerRunsPolicy:谁是添加这个任务的线程,谁去执行这个任务。

    3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早的任务,执行新的任务。

    4)ThreadPoolExecutor.DiscardPolicy:丢弃新的任务。

  • 相关阅读:
    【无标题】
    C++模板 —— 万字带你了解C++模板(蓝桥杯算法比赛必备知识STL基础)
    10、String、StringBuilder、StringBuffer的区别
    cubeIDE开发,I2C协议采集传感器数据(SHTC1、LTR-553ALS、BMP280、LSM6DSL、MMC3680KJ)
    2022最新版-李宏毅机器学习深度学习课程-P49 GPT的野望
    C#/.NET/.NET Core优秀项目和框架2024年1月简报
    隆云通负氧离子传感器
    Redis缓存(笔记一:缓存介绍和数据库启动)
    Mybatis源码分析
    代码随想录算法训练营day8 | 344.反转字符串、541. 反转字符串II、卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串
  • 原文地址:https://blog.csdn.net/LTY3301398968/article/details/133188323