스레드 풀의 "자바 병행 프로그래밍 아트"(II)

핵심 스레드 풀 집행자

자바 스레드는 작업 단위뿐만 아니라 집행 메커니즘 모두이다. 처음부터 JDK5, 작업 및 집행 메커니즘의 단위로 구분합니다. 메커니즘 실행 프로그램에 의해 수행 된 Runnable, Callable를 포함하여 작업의 단위.

실행기 프레임 구조 및 공정

그것은 다음과 같은 세 부분으로 구성

  • 작업 : Runnable를, 호출 인터페이스
  • 미션 : 집행자의 집행 인 및 상속 ExecutorService입니다
  • 비동기 계산 결과 : 미래 인터페이스, 미래 클래스의 인터페이스를 FutureTask

프로세스의 다양한 부분의 구현 :

  • 주 스레드는 작업을 생성 (또는 Runnable를 작업 개체 호출 인터페이스를 구현)합니다. 집행 도구가의 Runnable 객체 호출 가능 객체로 포장 될 수있다.
  • 직접 ExecutorSerivce 실행에의 Runnable 객체, 또는 당신은 ExecutorService를 실행에 제출 객체의 Runnable 또는 호출 가능 객체를 넣을 수 있습니다.
  • () 제출 미래 인터페이스를 구현하는 객체를 반환합니다. 메인 쓰레드가 작업 실행의 완료를 기다릴 FutureTask.get () 메소드를 실행할 수있다. 메인 쓰레드는 FutureTask.cancel () 메소드에 의해 수행되는 작업을 취소 할 수있다.

집행자의 프레임 부재

집행은 특수 용도 ThreadPoolExecutor에 다음과 같은 네 가지 범주를 만들 수 있습니다 :

  • ThreadPoolExecutor입니다
  • FixedThreadPool, 스레드 풀의 스레드 고정 된 수는 중복 작업 바운드 형식 블록 큐에 추가됩니다
  • SingleThreadPool, 단 하나의 스레드 풀 스레드는 중복 작업 바운드 형식 블록 큐에 추가됩니다
  • CachedThreadPool는 스레드 풀의 상한, SynchronousQueue는 큐의 사용도 없다

두 클래스를 만들 수 있습니다 집행 특별한 응용 프로그램을 스케줄 할 :

  • 로 스케줄
  • SingleThreadScheduledExecutor

다음 두 섹션은 집행자을 설명 분해 후에 실시됩니다

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搞懂,那么就不需要担心其他的特殊应用。

추천

출처www.cnblogs.com/codeleven/p/10963124.html