我是菜鸟:Executor框架

来源:互联网 发布:php编写九九乘法表for 编辑:程序博客网 时间:2024/05/17 03:07

在Java中,线程工作单元包括callable 和 runnable, 执行机制由Executor框架提供。
在java中,线程被一一映射为本地操作系统线程。当java线程启动时候会创建一个本地的操作系统线程,当java线程终止的时候,这个os线程也会被回收。应用程序通过Executor框架来控制上层的调度,而下层的调度由操作系统内核来控制,下层的调度不受应用程序的控制。
下图是任务的2级调度模型。
这里写图片描述

Excutor框架的3个部分

  1. 任务:需要实现Runnable或者Callable接口;
  2. 任务的执行: 包括顶级接口Executor, 以及其子接口ExecutorService接口。有2个类实现了 ExecutorService接口:ThreadPoolExecutor和ScheduledThreadPoolExecutor这2个类;
  3. 异步计算结果:Future接口和具体类FutureTask类。
    下面会逐步讲解每个部分的内部实现以及使用。

首先是使用步骤:
1. 创建任务对象:即实现了Runnable 或者Callable接口的类,注意利用Executors工具类可以将一个Runnable封装成一个Callable对象;
2. 提交任务;
ExecutorService.execute(Runnable command); 或者
ExecutorService.submit(Runnable task);或者
ExecutorService.submit(Callable task)
3. 对于ExecutorService.submit 提交的任务将会返回一个Future对象(或者FutureTask对象),然后执行FutureTask.get( )来获取结果。当然也可以执行FutureTask.cancel()取消任务的执行。

下面逐一介绍每个部分

ThreadPoolExecutor

一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。
线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。
下面是ThreadPoolExecutor的构造函数:

 public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }

下面是一些参数的含义,需要理解这些参数的含义:
@param corePoolSize the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
@param maximumPoolSize the maximum number of threads to allow in the pool
@param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle thread will wait for new tasks before terminating.
(多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将会被终止)
@param unit the time unit for the {@code keepAliveTime} argument (单位)
@param workQueue the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method.

通常有以下3中线程池:

FixedThreadPool

Executors.newFixedThreadPool(int) —-固定大小线程池
该线程池适用于需要限制线程数量的应用,例如负载比较重的服务器。

public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }

从上面的方法我们可以看到,核心线程数和最大的线程数都为参数nThread, 而如果没有新的任务,那么空闲线程则会被终止(keepAliveTime=0.)
需要注意的是这个线程池使用的是LinkedBlockingQueue, 这是一个无界队列(容量为Integer.MAX_VALUE)。由于LinkedBlockingQueue的使用,将会有如下的影响:
1. 当线程池中线程数量到达corePoolSize后,新任务将在无界队列中等待,线程池的中线程的数量不会超过corePoolSize.
2. 由于线程池中线程的数量不会超过corePoolSize, 那么将会导致maximumPoolSize, keepAliveTime为无效的参数
3. 运行中的FixedThreadPool不会拒绝任务。

SingleThreadExecutor

其与FixedThreadExecutor类似,唯一不同的是corePoolSize和maximumPoolSize的值都为1.

CachedThreadPool

该线程池是一个会根据需要创建线程的线程池。

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

其中corePoolSize为0,使用的阻塞队列为SynchronousQueue.
该线程池的执行步骤:
1. 首先执行SynchronousQueue.offer( Runnable task)(将任务插入队列),如果当前有空闲的线程正在执行SynchronousQueue.poll(取任务),那么主线程执行offer操作与空闲线程执行poll操作配对成功,主线程将任务提交给空闲线程执行,;
2. 如果当前没有空闲线程,此时会穿件一个新的线程来执行;

对于线程池的讲解就是一个段落。

SheduledThreadPoolExector详解

其为ThreadPoolExecutor的子类,主要用来在给定的延迟之后执行任务或者定期执行任务。
运行机制:
1. 当调用SheduledThreadPoolExector中的scheduleAtFixedRate( )或者scheduleWithFixedDelay( )方法后,会将ScheduleFutureTask添加到ScheduledThreadPoolExecutor的DelayQueue中;
2. 线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

下面是ScheduledThreadPoolExecutor中的线程执行周期任务的过程:
1. 获取已经到期的任务(DelayQueue.take( ));
2. 执行这个ScheduledFutureTask;
3. 修改ScheduledFutureTask的time变量为下次要执行的时间;
4. 将修改后的任务放回DelayQueue中;

下图是take方法的逻辑:
这里写图片描述
另外的add方法也是类似的,需要先获取锁,然后再添加节点。

FutureTask详解

FutureTask实现了Future接口和Runnable接口,因此FutureTask可以交给Executor执行,也可以由调用线程直接执行。FutureTask有下面3种状态:
1. 未启动。 在执行FutureTask.run()之前;
2. 以启动:FutureTask.run()方法执行的过程
3. 以完成。(包括正常结束,取消结束和异常结束。)

0 0
原创粉丝点击