Java线程池相关概念

线程池

1 概念

1.1 引入线程池

​ 在面向对象编程中,创建和销毁对象是很费时间的,对于线程来说也是如此,尤其是当线程中执行的是简单任务的话,则大部分的时间都花费在线程的创建和销毁上。所以就需要使用一种技术来解决这中资源浪费的情况,即池化技术–线程池,使用线程池时,会对线程进行复用,一个线程执行完当前任务后并不马上销毁,而是从任务队列中取出一个任务继续运行。这种做法提高了线程的利用率,也减少了系统开销。

1.2 线程池的作用

​ 线程池作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

1.3 优点

  1. 降低资源消耗:重用存在的线程,减少对象创建销毁的开销。
  2. 提高响应速度。可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
  4. 附加功能:提供定时执行、定期执行、单线程、并发数控制等功能。

2 线程池种类

JDK1.8提供了以下几个常用的线程池(要自己配置一个线程池是比较复杂的,所以JDK提供了几个比较常用的线程池):

  • newFixedThreadPool:是一个具有固定线程数的线程池,当所有线程都在执行任务时,新提交的任务会一直提交到阻塞队列中。当线程空闲时也不会释放线程,还会占用一定的系统资源。
  • newCacheThreadPool:是一个缓存线程池,会根据线程任务的数量来进行线程的创建和释放。
  • newSingleThreadExecutor:是一个固定线程数目始终只有1的线程池,所有任务都通过一个线程来执行,优点是可以保证任务按照提交顺序进行。
  • newScheduleThreadPool:是一个具有基础线程数并且具有缓存功能的线程池,并且支持周期性的任务执行。

3 其他概念

3.1 线程池的继承关系

继承图:
线程池继承体系

  • Executor:一个线程池中的顶层接口,但实际上它只是一个执行线程的工具,真正意义上是ExecutorService接口。
  • Executors:是一个工厂类,其中包含了一些静态方法,用来创建各种不同的线程池。
  • ExecutorService:真正意义上线程池接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法。
  • AbstractExecutorService:ExecutorService执行方法的默认实现。
  • ScheduledExecutorService:一个可定时调度任务的接口。
  • ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池。
  • ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象。
  • ForkJoinPool:将大任务分解成若干个小任务,当小任务均执行结束后,将任务做一个整合。

3.2 常用的四种线程池分析

在Executors工具类中,创建四种线程池的源码如下

常用线程池源码

​ 由源码可以看出,四种常见的线程池都直接或间接的继承自ThreadPoolExecutor类,而《阿里巴巴Java开发手册》中则强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式则必须更加明确线程池的运行规则,从而规避资源耗尽的风险。

分析ThreadPoolExecutor 类

  • 构造方法:正如源码所示,共四个构造方法,以下列代码的第一个构造方法为例(不使用任何默认值),分析其参数:
    • int corePoolSize:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize, 即使有其他空闲线程能够执行新来的任务, 也会继续创建线程;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。
    • int maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,直到当前线程数等于maximumPoolSize则停止创建;当阻塞队列是无界队列时,maximumPoolSize则不起作用, 因为无法提交至核心线程池的线程会一直持续地放入workQueue(阻塞队列)。
    • long keepAliveTime:多于corePoolSize(核心线程数)数目的线程空闲时的存活时间,即当线程没有任务执行时,该线程继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize(核心线程数)时才有用, 超过这个时间的空闲线程将被终止。
    • TimeUnit unit: keepAliveTime(最长存活时间)的单位。
    • workQueue:用来保存等待被执行的任务的阻塞队列,JDK中有以下几种:
      • ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO( First Input First Output,即先进先出、先来先服务)排序任务;
      • BlockingQueue workQueue:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
      • SynchronousQuene:一个不存储元素的阻塞队列(即只有一个位置),每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
      • priorityBlockingQuene:具有优先级的无界阻塞队列;
    • ThreadFactory threadFactory:创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。默认为DefaultThreadFactory。
    • RejectedExecutionHandler handler:线程池的饱和策略(可以自定义),当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
      • AbortPolicy:直接抛出异常,默认策略;
      • CallerRunsPolicy:用调用者所在的线程来执行任务;
      • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
      • DiscardPolicy:直接丢弃任务;
//用给定的初始参数创建一个新的 ThreadPoolExecutor 。 
public ThreadPoolExecutor(int corePoolSize,							//核心线程数
                              int maximumPoolSize,					//最大线程数
                              long keepAliveTime,					//最长存活时间
                              TimeUnit unit,						//存活时间单位
                              BlockingQueue<Runnable> workQueue,	//阻塞队列
                              ThreadFactory threadFactory,			//线程工厂
                              RejectedExecutionHandler handler) {	//饱和策略
    /*
    	使用两个if语句进行参数合法性判断
    */
        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;
    }
/**
	下面的三个都是使用一些默认值,然后调用第一个构造方法
*/
//创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝的执行处理程序。 
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
  	}
//创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。 
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
//使用给定的初始参数和默认拒绝的执行处理程序创建一个新的 ThreadPoolExecutor 。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

3.3 常用线程池的缺点

  • newFixedThreadPoolnewSingleThreadExecutor:
    主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至 OOM(内存溢出)。
  • newCachedThreadPoolnewScheduledThreadPool:
    主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM(内存溢出)。

3.4 线程池实现的原理

线程池原理图

3.5 线程池的状态

**线程池的5中状态:**RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。

  1. RUNNING:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。RUNNING也是线程池的初始状态。
  2. SHUTDOWN:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。调用shutdown()方法将状态转换至 SHUTDOWN。
  3. STOP:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
  4. TIDYING:当所有的任务已终止,任务数量为0时,线程池会变为TIDYING状态。TIDYING状态时,会执行terminated()方法,可以对该方法进行重载然后进行一些类似于资源清理的工作。线程池在SHUTDOWN状态且阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN转换为TIDYING。
  5. TERMINATED:线程池彻底终止,就变成TERMINATED状态。线程池处在TIDYING状态时,执行完terminated()(用于清理资源)方法之后,就会由 TIDYING 转换为TERMINATED。

3.5 关闭线程池

ExecutorService提供了shutDown()和shutDownNow()两个函数来关闭线程池,底层还是通过逐个调用线程的interrupt()函数来实现中断线程从而关闭线程池的。

  • shutdown函数会把线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。(即将当前所有线程任务执行完毕再销毁线程池
  • shutdownNow方法会先将线程池状态修改为STOP,然后调用线程池里的所有线程的interrupt方法,并把工作队列中尚未来得及执行的任务清空到一个List中返回,getTask()方法返回null,从而线程退出 。但是ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。(即直接销毁线程池,不会考虑是否有线程任务再执行
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答:Java线程池Java中的一个重点知识,并且在Java的工作中经常会遇到,因此在面试中也是必问的面试题目。以下是一些常见的Java线程池面试题: 1. 谈谈什么是线程池? 2. 为什么要使用线程池? 3. 你们哪些地方会使用到线程池? 4. 线程池有哪些作用? 5. 线程池的创建方式有哪些? 6. 线程池底层是如何实现复用的? 7. ThreadPoolExecutor核心参数有哪些? 8. 线程池创建的线程会一直在运行状态吗? 9. 为什么阿里巴巴不建议使用Executors? 10. 线程池的底层实现原理是什么? 11. 线程池队列满了,任务会丢失吗? 12. 线程池的拒绝策略类型有哪些? 13. 线程池如何合理配置参数? 这些问题涵盖了线程池的基本概念、使用场景、实现原理以及相关的配置和策略等方面的知识。了解这些问题能够帮助面试者更好地理解和应用Java线程池。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [java线程池面试题有哪些?java线程池常见面试题](https://blog.csdn.net/muli525/article/details/123553744)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [(一)【Java精选面试题】线程池底层实现原理(含答案)](https://blog.csdn.net/qq_30999361/article/details/124924343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值