Java中的线程池——Java并发编程的艺术读书笔记

本文深入探讨Java线程池的实现原理,包括核心线程、工作队列和饱和策略。详细介绍了线程池的创建、任务提交、关闭以及合理配置方法。建议使用有界队列以增强系统稳定性和预警能力。同时,讨论了如何监控线程池状态,以提升系统的可维护性。
摘要由CSDN通过智能技术生成


参考书籍:《Java并发编程的艺术》
  Java中的线程池是运用场景非常多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池,线程池主要有三个好处:
**1、降低资源消耗。**通过重复利用一创建的线程会降低线程创建和销毁带来的消耗。
**2、提高响应速度。**当任务到达时,任务可以不需要等到线程创建就能立即执行。
**3、提高线程的可管理性。**线程是稀缺资源,需要线程池统一分配、调优和监控。

1 线程池的实现原理

  当提交一个新任务到线程池中时,有下面的处理流程:
1、判断核心线程池里的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务;如果是,则进入下一个流程。
2、判断工作队列是否已满,如果未满则将新提交的任务存储在这个工作队列;如果已经满了,则进入下一个工作流程。
3、判断线程池里的线程是否已经满了,如果没有满,则创建一个新的工作线程来执行任务;如果满了,则交给饱和策略来处理这个任务。
在这里插入图片描述
  ThreadPoolExecutor执行execute方法分下面四种情况:
1、如果当前运行的线程数少于corePoolSize,则创建新县城来执行任务(这一步需要获取全局锁)
2、如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue
3、如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(这一步需要获取全局锁)
4、如果创建新线程使得当前运行线程超出maximumPoolSize,任务被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法
  ThreadPoolExecutor采取上述步骤的总体设计思路是在执行execute方法是,尽可能避免获取全局锁。在ThreadPoolExecutor完成预热后(当前运行的线程数大于等于corePoolSize),几乎所有的execute方法调用都是执行步骤2,因为步骤2不需要获取全局锁。

在这里插入图片描述
  ThreadPoolExecutor线程执行任务有两种情况:
1、在execute方法中创建一个线程,会让这个线程执行任务
2、在这个线程执行完下图中1的任务后,反复从BlockingQueue获取任务来执行
在这里插入图片描述

2 线程池的使用

2.1 线程池的创建

  创建线程池需要输入下面几个参数:

  • corePoolSize:线程池的基本大小。只要线程池中的线程少于这个参数,提交新任务时就会创建新线程来执行任务。
  • maximumPoolSize:线程池最大数量。如果队列满了并且创建的线程数小于这个参数,会继续创建新的线程执行任务。
  • keepAliveTime:线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。
  • unit:线程活动保持时间的单位。
  • workQueue:任务队列。用来保存等待执行任务的阻塞队列。
  • threadFactory:用于创建线程的工厂。
  • handler:饱和策略。当线程池和队列都满了,即处于饱和,这时采取这个策略处理提交的新任务。默认是AbortPolicy,无法处理新任务时抛出异常。
    在这里插入图片描述

2.2 向线程池提交任务

  有两个方法可以向线程池提交任务,分别为execute和submit。execute方法用于提交不需要返回值的任务,所以无法判断是否被线程池执行。submit方法用于提交需要返回值的任务,线程池会返回Future类型的对象,通过这个对象可以判断任务是否执行成功,可以通过future的get方法获得返回值,并且会阻塞进程直到任务完成。

2.3 关闭线程池

  调用shutdown或shutdownNow关闭线程池,原理是遍历线程池中工作的线程,诸葛调用interrupt方法中断线程,所以无法响应中断的方法无法终止。通常调用shutdown方法关闭线程池,线程池的状态设为SHUTDOWN,此时不能再往线程池添加任务,但是线程池不会立刻退出,而是等待线程池中的所有任务都执行完成。如果任务不一定要执行完,可以调用shutdownNow方法,线程池状态为STOP,停止所有线程,返回未执行的任务。

2.4 合理配置线程池

  建议使用有界队列,有界队列能增加系统的稳定性和预警能力,大小可以设置大一些,例如几千。下面是不同类型的任务的合理配置方法。

  • 任务的性质:CPU密集型、IO密集型和混合型
    CPU密集型任务应该配置尽可能小的线程,如N+1,因为CPU密集型计算量很大,CPU一直在执行任务,切换线程开销大,尽量避免切换线程。IO密集型任务由于CPU不是一直在执行任务,可以配置尽可能多的线程,防止CPU资源浪费,如2N。混合型任务可以拆分成CPU密集型和IO密集型。
  • 任务的优先级:高、中、低
    可以用优先级队列PriorityBlockingQueue来处理,可以让优先级高的任务先执行,但可能造成饥饿。
  • 任务的执行时间:长、中、短
    可以交给不同规模的线程池,或者使用优先级队列,执行时间短的优先执行。
  • 任务的依赖性:例如数据库连接
    依赖数据库的任务,因为可能等待数据库返回结果的时间较长,因此线程数可以设置大一些,这样才能更好利用CPU。

2.5 线程池的监控

  如果系统大量使用线程池,有必要对线程池进行监控,方便出现问题时快速定位。通过线程池提供的参数进行监控:

  • taskCount:线程池需要执行的任务数量
  • completedTaskCount:线程池已经完成的任务数量
  • largestPoolSize:线程池曾经创建的最大线程数量
  • getPoolSize:线程池的线程数量
  • getActiveCount:获取活动的线程数

  此外,也可以通过集成线程池来自定义线程池,在beforeExecution,afterExecution和Terminated等方法中加入想要的功能,例如监控任务平均执行时间等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值