【转】线程池和Executor框架

æ¬èæ维导å¾

 

一 使用线程池的好处

二 Executor 框架

2.1 简介

2.2 Executor框架结构(主要由三部分构成) 

2.3 Executor框架使用说明示意图

三 ThreadPoolExecutor详解

3.1 ThreadPoolExecutor类中重要的属性

3.2 ThreadPoolExecutor的构造方法

3.3 如何创建ThreadPoolExecutor

3.4 FixedThreadPool详解

3.5 SingleThreadExecutor详解

3.6 CachedThreadPool详解

3.7 ThreadPoolExecutor使用示例

3.7.1 示例代码

3.7.2 shutdown() vs shutdowNow()

3.7.3 isShutdown() vs isTerminated()

四 ScheduledThreadPoolExecutor详解

4.1 简介

4.2 ScheduledThreadPoolExecutor运行机制 

4.3 ScheduledThreadPoolExecutor执行周期任务的步骤

4.4 ScheduledThreadPoolExecutor使用示例

4.4.1 ScheduledExecutorService scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)方法

4.4.2 ScheduledExecutorService scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法

4.4.3 scheduleWithFixedDelay() vs scheduleAtFixedRate()

五 各种线程池的适用场景介绍


一 使用线程池的好处

线程池提供了一种限制和管理资源。每个线程池还维护了一些基本信息,如已完成任务的线程数量

这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:

降低资源消耗。通过重复利用已创建的线程减少创建和销毁线程的消耗
提高响应速度。当任务到达时候,不需要等待线程创建就能执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

二 Executor 框架

2.1 简介

Executor 框架是Java5之后引进的,在Java 5之后,通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。

补充:this逃逸是指在构造函数返回之前其他线程就持有该对象的引用. 调用尚未构造完全的对象的方法可能引发令人疑惑的错误。

2.2 Executor框架结构(主要由三部分构成) 

 1. 任务

执行任务需要实现的Runnable接口Callable接口
Runnable接口Callable接口实现类都可以被ThreadPoolExecutorScheduledThreadPoolExecutor执行。

两者的区别:

Runnable接口不会返回结果但是Callable接口可以返回结果。后面介绍Executors类的一些方法的时候会介绍到两者的相互转换。

 2. 任务执行

任务执行的核心接口是Executor接口,以及继承这个接口的子接口ExecutorService 

ScheduledThreadPoolExecutorThreadPoolExecutor这两个关键类实现了ExecutorService接口

ScheduledThreadPoolExecutor类描述:

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {

注意: ScheduledThreadPoolExecutor类继承ThreadPoolExecutor, ThreadPoolExecutor则继承了AbstractExecutorService类,AbstractExecutorService类则是实现ExecutorService接口。

ScheduledThreadPoolExecutor并且实现了接口ScheduledExecutorService,这个接口也是继承了ExecutorService接口,见下图所示

ThreadPoolExecutor类描述:

public class ThreadPoolExecutor extends AbstractExecutorService {
public abstract class AbstractExecutorService implements ExecutorService {

ä»»å¡çæ§è¡ç¸å³æ¥å£

3 异步计算的结果 

Future接口以及Future接口的实现类FutureTask类。
当我们把Runnable接口或Callable接口的实现类提交(调用submit方法)给ThreadPoolExecutor或ScheduledThreadPoolExecutor时,会返回一个FutureTask对象,FutureTask对象能够获取到线程的执行结果。
 以抽象类AbstractExecutorService的submit方法为例说明

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

2.3 Executor框架使用说明示意图

Executor æ¡æ¶ç使ç¨ç¤ºæå¾

1. 主线程首先要创建实现Runnable或者Callable接口的任务对象。
备注: 工具类Executors可以实现Runnable对象和Callable对象之间的相互转换。(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。

2. 然后可以把创建完成的Runnable对象直接交给ExecutorService执行(ExecutorService.execute(Runnable command));或者也可以把Runnable对象或Callable对象提交给ExecutorService执行(ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task))。

执行execute()方法和submit()方法的区别是什么呢?
1)execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
2)submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

3. 如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象(我们刚刚也提到过了执行execute()方法和submit()方法的区别,到目前为止的JDK中,返回的是FutureTask对象)。由于FutureTask实现了Runnable,程序员也可以创建FutureTask,然后直接交给ExecutorService执行。

4. 最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

三 ThreadPoolExecutor详解

3.1 ThreadPoolExecutor类中重要的属性

ThreadPoolExecutoræ¯è¾éè¦çå个å±æ§

3.2 ThreadPoolExecutor的构造方法

看下基本的构造器方法,其他的构造器方法都是会调用这个构造器方法

    /**
     * 用给定的初始参数创建一个新的ThreadPoolExecutor。

     * @param keepAliveTime 当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,
     *核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
     * @param unit  keepAliveTime参数的时间单位
     * @param workQueue 等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
     * 
     * @param threadFactory 执行者创建新线程时使用的工厂
     * @param handler RejectedExecutionHandler类型的变量,表示线程池的饱和策略。
     * 如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。
     * 线程池提供了4种策略:
        1.AbortPolicy:直接抛出异常,这是默认策略;
        2.CallerRunsPolicy:用调用者所在的线程来执行任务;
        3.DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
        4.DiscardPolicy:直接丢弃任务;
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

3.3 如何创建ThreadPoolExecutor

在《阿里巴巴Java开发手册》"并发处理"这一章中,指出线程必须由线程池提供,不允许在应用中显示创建线程。

为什么?

线程池具有减少系统消耗和提高响应时间的优点,不使用线程池可能出现系统创建大量同类线程消耗内存或者上下文切换的问题

另外《阿里巴巴Java开发手册》中强制线程池不允许使用 工具类Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors 返回线程池对象的弊端如下:

FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列的长度是Integer.MAX_VALUE,可能堆积大量的请求,导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

创建ThreadPoolExecutor方式1

éè¿æé æ¹æ³å®ç°

创建ThreadPoolExecutor方式2

这种使用Executors工具类创建的方式,不建议使用,主要看下几种类型线程池的构造方式

  • FixedThreadPool 
  • SingleThreadExecutor
  • CachedThreadPool

éè¿Executor æ¡æ¶çå·¥å·ç±»Executorsæ¥å®ç°

3.4 FixedThreadPool详解

FixedThreadPool可重用固定线程数量的线程池

   /**
     * 创建一个可重用固定数量线程的线程池
	 *在任何时候至多有n个线程处于活动状态
	 *如果在所有线程处于活动状态时提交其他任务,则它们将在队列中等待,
	 *直到线程可用。 如果任何线程在关闭之前的执行期间由于失败而终止,
	 *如果需要执行后续任务,则一个新的线程将取代它。池中的线程将一直存在
	 *知道调用shutdown方法
     * @param nThreads 线程池中的线程数
     * @param threadFactory 创建新线程时使用的factory
     * @return 新创建的线程池
     * @throws NullPointerException 如果threadFactory为null
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

另外还有一个FixedThreadPool的实现方法,和上面类似,不在赘述

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

从上面源码看出FixedThreadPool的corePoolSize和maximumPoolSize都被设置为nThreads。

FixedThreadPool的execute()方法运行示意图(该图片来源:《Java并发编程的艺术》):

FixedThreadPool的execute()方法运行示意图

执行过程说明

1. 线程池中的线程数量小于corepoolSize,每个到达的任务创建一个线程去执行

2. 线程池中的线程数量等于corePoolSize时候,到达的请求将会放入到队列中

3. 当有线程空闲,从队列中取出任务执行

FixedThreadPool使用无界队列 LinkedBlockingQueue(队列的容量为Intger.MAX_VALUE)作为线程池的工作队列会对线程池带来如下影响:

1. 当线程数目达到corePoolSize,因为使用无界队列,最大线程数量永远不会超过corePoolSize

2. 因为1,maximumPoolSize参数无效

3. 因为线程数量永远不会超过corePoolSize, 参数keepalivetime无效

4. 运行中的FixedThreadPool不会拒绝任务,会导致请求堆积,可能出现OOM问题。

3.5 SingleThreadExecutor详解

SingleThreadExecutor是使用单个线程的Executor,下面看下SingleThreadExecutor的实现

   /**
     *创建使用单个worker线程运行无界队列的Executor
	 *并使用提供的ThreadFactory在需要时创建新线程
     * @param threadFactory 创建新线程时使用的factory
     * @return 新创建的单线程Executor
     * @throws NullPointerException 如果ThreadFactory为空
     */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
   public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

从源码可以看出,SingleThreadExecutor的corePoolsize和axnumPoolSize都设置为1,队列使用的是无界队列(容量为Integer.MAX_VALUE),造成的影响和FixedThreadExecutor一样

SingleThreadExecutor的运行示意图(该图片来源:《Java并发编程的艺术》):
SingleThreadExecutor的运行示意图

上图说明

1. 任务到达,线程池中的线程数量为0,创建一个线程执行,并且线程池中只能创建这一个线程。

2. 当多个任务到达,线程忙碌,将任务加入到队列中

3. 线程空闲下来,从队列中取任务执行。

3.6 CachedThreadPool详解

CachedThreadPool是一个根据需要创建新线程的线程池,实现如下:

    /**
     * 创建一个线程池,使用ThreadFactory创建线程,并且在需要的情况下创建新的线程,否则使用之前创建的线程处理任务
     * @param threadFactory 线程创建工厂
     * @return 
     * @throws 当threadFactory为null,抛出NullPointerException
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

从源码看,corePoolSize为0,maxnumPoolSize设置为Integer.MAX_VALUE,当不断有新的任务加入,并且没有线程空闲的情况下,就会不断创建新的线程,这种情况会大量消耗系统资源,会导致OOM问题。

achedThreadPool的execute()方法的执行示意图(该图片来源:《Java并发编程的艺术》):
CachedThreadPool的execute()方法的执行示意图

上图说明:

 1. 因为CachedThreadPool的corePoolSize设置为0,当有任务加入,会被添加到队列中,当已创建的线程中有空闲的,

会从队列中poll任务执行

2. 当corePoolSize为0或者线程池中没有线程空闲,就会创建新的线程执行任务。

3.7 ThreadPoolExecutor使用示例

3.7.1 示例代码

创建一个Runnable实现类(或者创建Callable接口的实现类,两者的区别在于:Runnable不会返回执行结果,Callable会返回执行结果,两者可以通过类Executors的方法进行转换)

import java.util.Date;

/**
 * 这是一个简单的Runnable类,需要大约5秒钟来执行其任务。
 */
public class WorkerThread implements Runnable {

    private String command;

    public WorkerThread(String s) {
        this.command = s;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
        processCommand();
        System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
    }

    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return this.command;
    }
}

编写测试程序,以FixedThreadPool为例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExecutorDemo {

    public static void main(String[] args) {
        //创建一个FixedThreadPool对象
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            //创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
            Runnable worker = new WorkerThread("" + i);
            //执行Runnable
            executor.execute(worker);
        }
        //终止线程池
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("Finished all threads");
    }
}

输出示例:

pool-1-thread-5 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-3 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-2 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-4 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-1 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-4 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-1 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-2 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-3 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-2 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-1 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-4 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-3 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-1 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-2 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-3 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-4 End. Time = Thu May 31 10:23:02 CST 2018
Finished all threads

3.7.2 shutdown() vs shutdowNow()

shutdown(): 不能再向线程池提交新的任务,但是已经执行的任务将被完成

shutdownNow():停止执行中的任务,返回的是等待执行的任务列表

3.7.3 isShutdown() vs isTerminated()

isShutdown(): 调用shutdown()或者shutdownnow()返回true,表示程序正在关闭,并非所有任务完成执行

isTerminated():当所有线程执行的任务完成执行返回true,需要注意的是,必须在shutdown或者shutdownNow方法后调用,否则永远不返回true。

四 ScheduledThreadPoolExecutor详解

4.1 简介

ScheduledThreadPoolExecutor用来按照给定的延迟执行任务或者定期执行任务。

ScheduledThreadPoolExecutor使用的任务队列DelayQueue封装了一个PriorityQueue,PriorityQueue会对队列中的任务进行排序,执行所需时间短的放在前面先被执行(ScheduledFutureTask的time变量小的先执行),如果执行所需时间相同则先提交的任务将被先执行(ScheduledFutureTask的squenceNumber变量小的先执行)。

  • ScheduledThreadPoolExecutor和Timer的比较:
  • Timer对时钟变化敏感,ScheduledThreadPoolExecutor则不会;
  • Timer只有一个执行线程,因此长时间运行的任务可以延迟其他任务。 ScheduledThreadPoolExecutor可以配置任意数量的线程。 此外,如果你想(通过提供ThreadFactory),你可以完全控制创建的线程;
  • 在TimerTask中抛出的运行时异常会杀死一个线程,从而导致Timer死机:-( …即计划任务将不再运行。ScheduledThreadExecutor不仅捕获运行时异常,还允许您在需要时处理它们(通过重写afterExecute方法 ThreadPoolExecutor)。抛出异常的任务将被取消,但其他任务将继续运行。

综上,在JDK1.5之后,你没有理由再使用Timer进行任务调度了。

备注: Quartz是一个由java编写的任务调度库,由OpenSymphony组织开源出来。在实际项目开发中使用Quartz的还是居多,比较推荐使用Quartz。因为Quartz理论上能够同时对上万个任务进行调度,拥有丰富的功能特性,包括任务调度、任务持久化、可集群化、插件等等。

看下,ScheduledThreadPoolExecutor的构造方法,主要是看corePoolSize,maxnumPoolSize参数设置,看其中一个构造方法就行。

 public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

 corePoolSize设置为0,maxnumPoolSize设置为Integer.MAX_VALUE,当提交任务的速度高于线程池处理的速度,会不断创建新的线程,大量消耗系统资源。

4.2 ScheduledThreadPoolExecutor运行机制 

ScheduledThreadPoolExecutorè¿è¡æºå¶

ScheduledThreadPoolExecutor的执行分为两步:

1.  当调用了ScheduledThreadPoolExecutor的scheduleAtFixedRate()或者sheduleWithFixedDelay()方法时,会向DelayQueue中添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask 

2. 线程池中的线程从队列中取出ScheduledFutureTask 然后执行。

ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下修改:

  • 使用 DelayQueue 作为任务队列;
  • 获取任务的方不同
  • 执行周期任务后,增加了额外的处理

4.3 ScheduledThreadPoolExecutor执行周期任务的步骤

ScheduledThreadPoolExecutoræ§è¡å¨æä»»å¡ç步骤

线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前系统的时间;
线程1执行这个ScheduledFutureTask;
线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间;
线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。

4.4 ScheduledThreadPoolExecutor使用示例

创建一个简单的实现Runnable接口的类(我们上面的例子已经实现过)

测试程序使用ScheduledExecutorService和ScheduledThreadPoolExecutor实现的java调度。
 

/**
 * 使用ScheduledExecutorService和ScheduledThreadPoolExecutor实现的java调度程序示例程序。
 */
public class ScheduledThreadPoolDemo {

    public static void main(String[] args) throws InterruptedException {

        //创建一个ScheduledThreadPoolExecutor对象
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        //计划在某段时间后运行
        System.out.println("Current Time = "+new Date());
        for(int i=0; i<3; i++){
            Thread.sleep(1000);
            WorkerThread worker = new WorkerThread("do heavy processing");
            //创建并执行在给定延迟后启用的单次操作。 
            scheduledThreadPool.schedule(worker, 10, TimeUnit.SECONDS);
        }

        //添加一些延迟让调度程序产生一些线程
        Thread.sleep(30000);
        System.out.println("Current Time = "+new Date());
        //关闭线程池
        scheduledThreadPool.shutdown();
        while(!scheduledThreadPool.isTerminated()){
            //等待所有任务完成
        }
        System.out.println("Finished all threads");
    }

}

运行结果:

Current Time = Wed May 30 17:11:16 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:11:27 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:11:28 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:11:29 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:11:32 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:11:33 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:11:34 CST 2018
Current Time = Wed May 30 17:11:49 CST 2018
Finished all threads

4.4.1 ScheduledExecutorService scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)方法

我们可以使用ScheduledExecutorService scheduleAtFixedRate方法来安排任务在初始延迟后运行,然后在给定的时间段内运行。

时间段是从池中第一个线程的开始,因此如果您将period指定为1秒并且线程运行5秒,那么只要第一个工作线程完成执行,下一个线程就会开始执行。

for (int i = 0; i < 3; i++) {
	Thread.sleep(1000);
	WorkerThread worker = new WorkerThread("do heavy processing");
	// schedule task to execute at fixed rate
	scheduledThreadPool.scheduleAtFixedRate(worker, 0, 10,
	TimeUnit.SECONDS);
}

运行结果:

Current Time = Wed May 30 17:47:09 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:10 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:11 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:47:12 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:15 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:16 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:47:17 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:20 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:47:21 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:22 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:25 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:47:26 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:27 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:30 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:47:31 CST 2018
pool-1-thread-5 Start. Time = Wed May 30 17:47:32 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:35 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:47:36 CST 2018
pool-1-thread-5 End. Time = Wed May 30 17:47:37 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:40 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:41 CST 2018
Current Time = Wed May 30 17:47:42 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:45 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:46 CST 2018
Finished all threads

Process finished with exit code 0

 

4.4.2 ScheduledExecutorService scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法

ScheduledExecutorService scheduleWithFixedDelay方法可用于以初始延迟启动周期性执行,然后以给定延迟执行。 延迟时间是线程完成执行的时间。

for (int i = 0; i < 3; i++) {
	Thread.sleep(1000);
	WorkerThread worker = new WorkerThread("do heavy processing");
	scheduledThreadPool.scheduleWithFixedDelay(worker, 0, 1,
	TimeUnit.SECONDS);
}

运行结果:

Current Time = Wed May 30 17:58:09 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:10 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:11 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:58:12 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:15 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:16 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:16 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:58:17 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:17 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:18 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:21 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:22 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:22 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:23 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:23 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:24 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:27 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:28 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:28 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:29 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:29 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:30 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:33 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:34 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:34 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:35 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:35 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:36 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:39 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:40 CST 2018
pool-1-thread-5 Start. Time = Wed May 30 17:58:40 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:41 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:41 CST 2018
Current Time = Wed May 30 17:58:42 CST 2018
pool-1-thread-5 End. Time = Wed May 30 17:58:45 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:46 CST 2018
Finished all threads

4.4.3 scheduleWithFixedDelay() vs scheduleAtFixedRate()

scheduleAtFixedRate(…)将延迟视为两个任务开始的差异(定期调用)
scheduleWithFixedDelay(…)将延迟视为一个任务结束与下一个任务开始之间的差异

scheduleAtFixedRate(): 创建并执行在给定的初始延迟之后,随后以给定的时间段首先启用的周期性动作; 那就是执行将在initialDelay之后开始,然后initialDelay+period ,然后是initialDelay + 2 * period ,等等。 如果任务的执行遇到异常,则后续的执行被抑制。 否则,任务将仅通过取消或终止执行人终止。 如果任务执行时间比其周期长,则后续执行可能会迟到,但不会同时执行。
scheduleWithFixedDelay() : 创建并执行在给定的初始延迟之后首先启用的定期动作,随后在一个执行的终止和下一个执行的开始之间给定的延迟。 如果任务的执行遇到异常,则后续的执行被抑制。 否则,任务将仅通过取消或终止执行终止。
 

五 各种线程池的适用场景介绍

FixedThreadPool: 适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景。它适用于负载比较重的服务器;

SingleThreadExecutor: 适用于需要保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景。

CachedThreadPool: 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;

ScheduledThreadPoolExecutor: 适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景,

SingleThreadScheduledExecutor: 适用于需要单个后台线程执行周期任务,同时保证顺序地执行各个任务的应用场景。

转载: https://blog.csdn.net/qq_34337272/article/details/79959271 
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值