《java并发编程的艺术》Executor框架

在HotSport VM线程模型中,java线程被一对一映射为本地操作系统线程。

分两层:
顶层:Executor框架调度将多线程程序分解的若干任务映射为固定数量的线程。
底层:操作系统内核将这些线程映射到硬件处理器上。

Executor框架结构

  • 任务。包括被执行任务需要实现的接口:Runnable接口和Callable接口
  • 任务的执行。包括Executor,和ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)
  • 异步计算的结果。包括接口Future和实现Future接口的FutureTask类

Executor框架的成员

  1. ThreadPoolExecutor通常由工厂类Executors创建,Executors能创建3中类型的ThreadPoolExecutor。
    1)FixedThreadPool。适用于负载比较重的服务器。有两个构造方法:
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads,ThreadFactory threadFactory)

本质上:

new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);

2)SingleThreadPool。适用于需要保证顺序执行各个任务;并且在任意时间点,没有多线程活动的场景。两个构造方法:

public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

本质上:

new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));

3)CachedThreadPool。大小无界,适用于执行很多的短期异步任务的小程序,或负载较轻的服务器。两个构造方法:

public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

本质上:

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory)
  1. ScheduledThreadPoolExecutor extends ThreadPoolExecutor.首先要了解,这个是继承父类ThreadPoolExecutor的。
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }

1)ScheduledThreadPool适用于需要多个后台线程执行执行周期任务,同时限制后台线程个数。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)

本质上:

new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue())
new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(),threadFactory)

2)SingleThreadScheduledExecutor适用于单个线程执行周期任务,保证顺序。

public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

本质上:

new DelegatedScheduledExecutorService(new ThreadPoolExecutor(1, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue()))
new DelegatedScheduledExecutorService(new ThreadPoolExecutor(1, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(),threadFactory))
  1. Future接口
    Future接口和实现类FutureTask类用来表示异步计算的结果。当我们调用submit方法时返回一个FutureTask对象。注意,在API中能看到返回的是一个实现Future接口的对象,将来可能会对Future实现类进行拓展。

  2. Runnable和Callable
    Runnable无返回,Callable返回Future。
    Executors能把Runnable包装成Callable:

public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task,T result)

ScheduledThreadPoolExecutor

主要用来在给定的延迟之后运行任务,或者定期执行任务。

DelayQueue是一个无界队列。

执行分两部分:
1. 当调用ScheduledThreadPoolExecute的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecute的DelayQueue添加一个实现了RunnableScheduleFuture接口的ScheduledFutureTask。
2. 线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

ScheduledFutureTask有3个成员变量:
- long型的time,表示这个任务将要被执行的具体时间。
- long型的sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号
- long型的period,表示任务执行的间隔周期。

DelayQueue封装了一个PriorityQueue,这个PrioriyQueue会对队列中的ScheduledFutureTask进行排序。排序时,time小的排在前面,time相同的则比较sequenceNumber,小的排前面。

执行步骤:
1. 从DelayQueue获取已到期的ScheduleFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前时间。
2. 执行ScheduledFutureTask
3. 修改ScheduledFutureTask的time为下次将要执行的时间
4. 把修改time后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())

获取任务3大步骤:
1. 获取Lock
2. 获取周期任务
- 如果队列为空,当前线程到Condition中等待;否则执行下面一点
- 如果队列的头元素time时间比当前时间大,则到Condition中等待到time时间,否则执行下一点
- 获取队列的头元素,如果队列不为空,则唤醒Condition中等待的所有线程
3. 释放Lock

FutureTask

FutureTask除了实现Future接口外,还实现Runnable接口。所以FutureTask可以交给Executor执行,也可以调用线程直接执行(FutureTask.run()).根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态:1. 未启动 2. 已启动 3. 已完成(正常完成、FutureTask.cancel()、抛异常结束)

FutureTask的实现基于AQS。每一个基于AQS实现的同步器都会包含两种类型的操作:
1. 至少一个acquire操作。这个操作阻塞调用线程,除非/直到AQS的状态允许这个线程执行。FutureTask中为get()/get(long timeout,TimeUnit unit)方法调用。
2. 至少一个release操作。这个操作改变AQS的状态,改变后的状态可允许一个或多个阻塞线程被解除阻塞。FutureTask中为run方法和cancel方法。

猜你喜欢

转载自blog.csdn.net/saywhat_sayhello/article/details/81141804