转载

Java并发编程的艺术(十二)——线程池

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
复制代码
  • ●corePoolSize:基本线程数,代表线程池的基本大小。当线程池里的工作线程数小于基本线程数时,总是会创建新的线程来处理新来的任务。如果调用线程池的prestartAllCoreThreads(),线程池会提前创建好所有的基本线程。
  • ●maximumPoolSize:最大线程数,代表线程池允许创建的最大线程数目。当任务队列已满,且已创建的线程数小于最大线程数,则线程池会创建新的线程执行任务。需要注意的是,对于无界队列,该值无效。
  • ●keepAliveTime:线程池中除基本线程外,其余空闲线程的保活时间。超过这个时间后,空闲线程将会被终止。
  • ●timeUnit:线程保活时间的单位,可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLSECONDS)、微秒(MICROSECONDS)、纳秒(NANOSECONDS)。
  • ●runnableTaskQueue:任务队列,当线程池里的基本线程都在执行任务时,新来的任务将被添加到任务队列里等待执行。该队列有以下几种选择:
    • 1.ArrayBlockingQueue :基于数组的有界阻塞队列,FIFO。
    • 2.LinkedBlockingQueue :基于链表的无界阻塞队列,FIFO,吞吐量通常要高于 ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
    • 3.SynchronousQueue :一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
    • 4.PriorityBlockingQueue :一个具有优先级的无线阻塞队列。 ●handler:饱和策略,当队列和线程池都满了,新提交的任务通过饱和策略进行处理。饱和策略有以下4种:
      • ①.AbortPolicy :默认的策略,作用是直接抛出异常。
      • ②.CallerRunsPolicy :只用调用者所在线程来运行任务。
      • ③.DiscardOldestPolicy :丢弃队列里最近的一个任务,并执行当前任务。
      • ④.DiscardPolicy :不处理,丢弃掉。

向线程池提交任务

可以使用两个方法向线程池提交任务,分别是execute()和submit()方法。

  • ●execute():用于提交不需要返回值的任务,无法判断任务是否被线程池执行成功。
  • ●submit():用于提交需要返回值的任务,返回值是有个Future类型的对象,通过这个对象可以判断任务是否执行成功,并且通过这个Future类型对象的get()方法可以阻塞当前线程直至任务执行成功,该方法会有返回值。

关闭线程池

可以通过调用线程池的shutdown()或者shutdownNow()方法来关闭线程池。他们的原理都是通过遍历线程池中的线程,然后逐个调用线程的interrupt()方法来中断线程。但这两个方法存在着差别:

  • shutdown():该方法中断未执行任务的线程,不中断正在执行任务的线程。
  • shutdownNow():该方法中断所有正在执行或已暂停的任务线程。

合理配置线程池

任务的性质可分为:CPU密集型任务、IO密集型任务、混合型任务。线程池的配置需要考虑任务特性。

  • ●CPU密集型任务:应分配尽可能小的线程池,如线程池大小配置为CPU核心数+1,如此可避免频繁的线程上下文切换造成的开销。
  • ●IO密集型任务:应分配尽可能大的线程池,如线程池大小配置为CPU核心数*2,因为IO密集型任务对CPU的使用较少,创建较多的线程可提高对CPU的利用率。
  • ●混合型任务:可以将任务拆分人CPU密集型任务和IO密集型任务,然后分别用两个线程池去处理,只有分完之后的两个任务的执行时间相差不大,那么执行起来比串行执行要高效。当两个任务的执行时间相差较大时,执行快的一方要等待执行慢的一方,在效率上并没有多大提升。
原文  https://juejin.im/post/5d16bc7ee51d45108223fca8
正文到此结束
Loading...