"Java concurrent programming art" of the thread pool (II)

The core thread pool Executor

Java thread is both a unit of work, but also enforcement mechanisms. JDK5 from the start, the unit of work and enforcement mechanisms separate. Unit of work including Runnable, Callable, performed by the mechanisms Executor.

Executor frame structures and processes

It consists of the following three parts:

  • Task: Runnable, Callable Interface
  • Mission: Executor, the Executor and inheritance ExecutorService
  • Results asynchronous computation: Future interface, the interface of the Future classes FutureTask

The implementation of various parts of the process:

  • The main thread to create a task (or implement Runnable task object Callable interface). Executors tools can be packaged as a Runnable object Callable object.
  • The Runnable objects directly to ExecutorSerivce execution, or you can put an object Runnable or Callable object submitted to ExecutorService execution.
  • submit () returns an object that implements the Future interface. The main thread can execute FutureTask.get () method to wait for the completion of the task execution. The main thread can also cancel tasks performed by FutureTask.cancel () method.

Executor frame members

Executors can create the following four categories ThreadPoolExecutor special applications:

  • ThreadPoolExecutor
  • FixedThreadPool, a fixed number of threads in the thread pool, redundant tasks will be added unbounded blocking queue
  • SingleThreadPool, only a single thread pool thread, redundant tasks will be added unbounded blocking queue
  • CachedThreadPool, there is no upper limit of the thread pool, use of SynchronousQueue queue

Executors can create two classes ScheduledThreadPoolExecutor special applications:

  • ScheduledThreadPoolExecutor
  • SingleThreadScheduledExecutor

The following two sections will be carried out after the decomposition explain Executor

ThreadPoolExecutor下的特殊应用(任务执行 部分)

FixedThreadPool

FixedThreadPool的创建方法,FixedThreadPool其实就是ThreadPoolExecutors的特殊用法:

// 只能通过Executors.newFixedThreadPool()创建
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

主要需要注意的是LinkedBlockingQueue这个无界阻塞队列,该阻塞队列会造成以下几个影响:

  • 执行中的线程数达到corePoolSize,新任务会在无界队列中等待,所以线程数不会超过corePoolSize
  • 因为不会创建更多的线程,所以maximumPoolSize和keepAliveTime参数就没什么作用了
  • 因为是无界队列,所以也不需要用到RejectPolicyHandler

SingleThreadExecutor

该类也是ThreadPoolExecutor的特殊用法

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

相比FixedThreadPool,除一个正在执行任务的线程,其他任务都会一一加入队列中等待执行。

CachedThreadPool

该类比较特殊,是FixedThreadPool的无限版本:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

一手SynchronousQueue,保证每次offer任务时,需要一个线程poll该任务,否则SynchronousQueue会阻塞。所以其应用场景就是在任务量多,但是执行时间短的场景下。但是我认为这个方法有点无用,普通的线程池可以完全胜任该工作

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor虽然不是对ThreadPoolExecutor的参数进行调整,但是对其执行流程进行了一个调整。该类主要用来在给定的延时之后执行任务或定期执行任务。ScheduledThreadPoolExecutor和Timer类似,但是更强大。Timer在单线程下执行,如果前一个任务执行时间过长,会影响下一个任务的执行。

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    // 继承ThreadPoolExecutor
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

因为是无界队列,所以maximumPoolSize,aliveKeepTime和workQueue三个参数没什么意义(根据流程判断用不到它们)

ScheduledThreadPoolExecutor的执行主要分成两个部分:

  • 当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向DelayQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask
  • 线程池会从DelayQueue中获取ScheduledFutureTask,然后执行任务

ScheduledThreadPoolExecutor在ThreadPoolExecutor的基础上做了以下修改:

  • 使用DelayQueue作为任务队列
  • 获取任务的方式不同
  • 执行任务时会做一些修改

接下来就围绕着上面三点不同点,研究ScheduledThreadPoolExecutor

DelayQueue作为任务队列

具体的内容看这篇

DelayQueue封装了一个PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序。time小的排在前面(时间早的任务先被执行)。如果两个task的taim相同就比较sequenceNumber,sequenceNumber小的排在前面。

获取任务的方式

线程池总从DelayQueue中获取流程:

  1. 线程1从DelayQueue中获取已到期的ScheduledFutureTask
  2. 线程1执行该task
  3. 线程1修改该task的time变量为下一次执行的时间
  4. 线程1把这个修改time后的task放回DelayQueue中

FutureTask(异步结果 部分)

该类除了Future接口外还实现了Runnable接口。因此可以将FutureTask提交给线程池使用。该任务如果有多个线程尝试去执行,最终只会有一个线程可以执行,其他线程只能等待该任务执行完毕才能继续执行。

FutureTask可以处于以下三种状态:

  • 未启动。在run()方法还没执行之前,FutureTask处于未启动状态
  • 已启动。在run()方法执行过程中,FutureTask处于已启动状态
  • 已完成。在run()方法执行完后正常结束、被取消、抛出异常。

状态迁移示例图
当FutureTask处于未启动或已启动状态,调用get()方法,会让调用线程进入阻塞状态;当FutureTask处于已完成状态,阻塞的get()方法会立即返回

执行示意图
不同状态下调用get/cancel方法,会有不同的影响

总结

无论是ScheduledThreadPoolExecutor还是FixedThreadExecutor等,都是ThreadPoolExecutor的特殊用法,只要把ThreadPoolExecutor搞懂,那么就不需要担心其他的特殊应用。

Guess you like

Origin www.cnblogs.com/codeleven/p/10963124.html