java并发编程的艺术第九章——java中的线程池

1、线程池的三个好处:

  • 降低资源消耗。可以重复利用已创建的线程,降低创建/销毁线程的开销
  • 提高响应速度。
  • 提高线程的可管理性。统一分配、调优、监控。

2、线程池的处理流程

新任务提交至线程池后的处理流程:

  • 1.核心线程池是否已满,如果没满,则创建一个线程执行任务,如果满了,则进入下一个流程
  • 2.判断队列是否已经满了,如果没满,则将任务存储在队列中,如果满了,则进入下一个流程
  • 3.判断线程池是否已经满了,如果没满,则创建线程执行任务,如果满了,则按照策略处理无法执行的任务。

3、线程池的使用

3.1、线程池的创建
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize:线程池的基本大小
  • maximumPoolSize:线程池的最大线程数
  • keepAliveTime:线程的活动的保持时间。当线程空闲之后,保持存活的时间,如果任务量大,而任务的执行时间又短的话,可以增大线程池的存活时间,提高线程的利用率
  • TimeUnit:线程活动保持时间的单位
  • BlockingQueue:用于存放任务的阻塞队列,队列的选择可以参考java中的阻塞队列
  • ThreadFactory:用于设置创建线程的工厂。
  • RejectedExecutionHandler:饱和策略。即当线程和队列都已经满了的时候,应该采取什么样的策略来处理新提交的任务,默认策略是AbortPolicy(抛出异常),其他的策略还有:CallerRunsPolicy(只用调用者所在线程来运行任务),DiscardOldestPolicy(丢弃队列里最近的一个任务,并执行当前任务),DiscardPolicy(不处理,丢弃掉)
3.2、向线程池提交任务

提交任务有两种方法,分别是execute()和submit()。

execute()方法适用于任务提交之后没有返回值的这种情况,因为没有返回值,所以提交任务之后我们也无法判断任务是否执行成功。

submit方法适用于提交任务之后有返回值的情况,它会返回一个Future类型的对象,可以通过future.get()方法来获取返回值,get()会阻塞线程直到任务完成。

3.3、关闭线程池

关闭线程池有两种方法,分别是shutdown()和shutdownNow(),它们的原理都是遍历线程池中所有的线程,分别调用每个线程池的interrupt()方法来中断线程。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

3.4、合理的配置线程池

线程池配置的分析角度

  • 任务的性质:IO密集型还是CPU密集型,cup密集型可以配置小一点的线程数,io密集型可以配置多的线程数
  • 任务的优先级:可以用PriorityBlockingQueue队列来处理
  • 任务处理时间的长短
  • 任务的依赖性:比如说是否依赖数据库连接

线程池的任务队列的选择建议使用有界队列,因为如果任务太多,有界队列可以抛出异常便于我们排查,而无界队列会使队列中的任务越来越多,可能导致撑满内存,导致整个系统的不可用。

3.5、线程池的监控

你可能感兴趣的:(java并发编程的艺术第九章——java中的线程池)