https://www.cnblogs.com/gustavo

Gustavo's Blog

人类的赞歌是勇气的赞歌!

线程池简介

介绍

线程池同其他池技术一样属于提高效率,减少性能开销的池化资源技术。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。Executor框架让你可以创建不同的线程池。比如单线程池,数目固定的线程池或者是缓存线程池。
在面向对象编程中,创建和销毁对象是很费时间的,如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些”池化资源”技术产生的原因。
线程池的优点:
重用线程池中的线程,减少因对象创建,销毁所带来的性能开销;
能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞;
能够多线程进行简单的管理,使线程的使用简单、高效。


Executor 框架

Executor
执行器接口,也是最顶层的抽象核心接口, 分离了任务和任务的执行。
ExecutorService
在Executor的基础上提供了执行器生命周期管理,任务异步执行等功能。
ScheduledExecutorService
在ExecutorService基础上提供了任务的延迟执行/周期执行的功能。
Executors
生产具体的执行器的静态工厂
ThreadFactory
线程工厂,用于创建单个线程,减少手工创建线程的繁琐工作,同时能够复用工厂的特性。
AbstractExecutorService
ExecutorService的抽象实现,为各类执行器类的实现提供基础。
ThreadPoolExecutor
线程池Executor,也是最常用的Executor,可以以线程池的方式管理线程。
ScheduledThreadPoolExecutor
在ThreadPoolExecutor基础上,增加了对周期任务调度的支持。
ForkJoinPool
Fork/Join线程池,在JDK1.7时引入,时实现Fork/Join框架的核心类。

<wiz_code_mirror>
 
 
 
 
 
 
 
public interface Executor {
    /**
     * 执行给定的Runnable任务.
     * 根据Executor的实现不同, 具体执行方式也不相同.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be accepted for execution
     * @throws NullPointerException       if command is null
     */
    void execute(Runnable command);
}
 
 

解决的问题

Executor是JDK1.5时,随着J.U.C引入的一个接口,引入该接口的主要目的是解耦任务本身和任务的执行。
而Executor接口解耦了任务和任务的执行,该接口只有一个方法,入参为待执行的任务:
我们可以像下面这样执行任务,而不必关心线程的创建:

<wiz_code_mirror>
 
 
 
 
 
 
 
Executor executor = someExecutor;       // 创建具体的Executor对象
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...
 
 

组成部分

Executor框架包括3大部分:
1 任务
执行任务需要实现的 Runnable 接口 或 Callable接口。Runnable 接口或 Callable 接口 实现类都可以被 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。
2 任务的执行
包括任务执行机制的核心接口 Executor ,以及继承自 Executor 接口的 ExecutorService 接口。ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 这两个关键类实现了 ExecutorService 接口。

(3)异步计算的结果。
Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步计算的结果。
当我们把 Runnable接口 或 Callable 接口 的实现类提交给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)

框架使用


1 主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
2 把创建完成的实现 Runnable/Callable接口的 对象直接交给 ExecutorService 执行: ExecutorService.execute(Runnable command))或者也可以把 Runnable 对象或Callable 对象提交给 ExecutorService 执行(ExecutorService.submit(Runnable task)或 ExecutorService.submit(Callable task))。
3 如果执行 ExecutorService.submit(…),ExecutorService 将返回一个实现Future接口的对象(我们刚刚也提到过了执行 execute()方法和 submit()方法的区别,submit()会返回一个 FutureTask 对象)。由于 FutureTask 实现了 Runnable,我们也可以创建 FutureTask,然后直接交给 ExecutorService 执行。
4 最后,主线程可以执行 FutureTask.get()方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

增强的Executor——ExecutorService

Executor接口提供的功能很简单,为了对它进行增强,J.U.C又提供了一个名为ExecutorService接口,ExecutorService也是在JDK1.5时,随着J.U.C引入的,ExecutorService继承了Executor,它在Executor的基础上增强了对任务的控制,同时包括对自身生命周期的管理,主要有四类:
关闭执行器,禁止任务的提交;
监视执行器的状态;
提供对异步任务的支持;
提供对批处理任务的支持。

<wiz_code_mirror>
 
 
 
 
 
 
 
public interface ExecutorService extends Executor {
 
    /**
     * 关闭执行器, 主要有以下特点:
     * 1. 已经提交给该执行器的任务将会继续执行, 但是不再接受新任务的提交;
     * 2. 如果执行器已经关闭了, 则再次调用没有副作用.
     */
    void shutdown();
 
    /**
     * 立即关闭执行器, 主要有以下特点:
     * 1. 尝试停止所有正在执行的任务, 无法保证能够停止成功, 但会尽力尝试(例如, 通过 Thread.interrupt中断任务, 但是不响应中断的任务可能无法终止);
     * 2. 暂停处理已经提交但未执行的任务;
     *
     * @return 返回已经提交但未执行的任务列表
     */
    List<Runnable> shutdownNow();
 
    /**
     * 如果该执行器已经关闭, 则返回true.
     */
    boolean isShutdown();
 
    /**
     * 判断执行器是否已经【终止】.
     * <p>
     * 仅当执行器已关闭且所有任务都已经执行完成, 才返回true.
     * 注意: 除非首先调用 shutdown 或 shutdownNow, 否则该方法永远返回false.
     */
    boolean isTerminated();
 
    /**
     * 阻塞调用线程, 等待执行器到达【终止】状态.
     *
     * @return {@code true} 如果执行器最终到达终止状态, 则返回true; 否则返回false
     * @throws InterruptedException if interrupted while waiting
     */
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
 
    /**
     * 提交一个具有返回值的任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回task的返回值.
     *
     * @param task 待提交的任务
     * @param <T>  任务的返回值类型
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    <T> Future<T> submit(Callable<T> task);
 
    /**
     * 提交一个 Runnable 任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回给定的结果(入参时指定).
     *
     * @param task   待提交的任务
     * @param result 返回的结果
     * @param <T>    返回的结果类型
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    <T> Future<T> submit(Runnable task, T result);
 
    /**
     * 提交一个 Runnable 任务用于执行.
     * 注意: Future的get方法在成功完成时将会返回null.
     *
     * @param task 待提交的任务
     * @return 返回该任务的Future对象
     * @throws RejectedExecutionException 如果任务无法安排执行
     * @throws NullPointerException       if the task is null
     */
    Future<?> submit(Runnable task);
 
    /**
     * 执行给定集合中的所有任务, 当所有任务都执行完成后, 返回保持任务状态和结果的 Future 列表.
     * <p>
     * 注意: 该方法为同步方法. 返回列表中的所有元素的Future.isDone() 为 true.
     *
     * @param tasks 任务集合
     * @param <T>   任务的返回结果类型
     * @return 任务的Future对象列表,列表顺序与集合中的迭代器所生成的顺序相同,
     * @throws InterruptedException       如果等待时发生中断, 会将所有未完成的任务取消.
     * @throws NullPointerException       任一任务为 null
     * @throws RejectedExecutionException 如果任一任务无法安排执行
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
 
    /**
     * 执行给定集合中的所有任务, 当所有任务都执行完成后或超时期满时(无论哪个首先发生), 返回保持任务状态和结果的 Future 列表.
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
 
    /**
     * 执行给定集合中的任务, 只有其中某个任务率先成功完成(未抛出异常), 则返回其结果.
     * 一旦正常或异常返回后, 则取消尚未完成的任务.
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
 
    /**
     * 执行给定集合中的任务, 如果在给定的超时期满前, 某个任务已成功完成(未抛出异常), 则返回其结果.
     * 一旦正常或异常返回后, 则取消尚未完成的任务.
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
 
 

周期任务的调度——ScheduledExecutorService

在工业环境中,我们可能希望提交给执行器的某些任务能够定时执行或周期性地执行,这时我们可以自己实现Executor接口来创建符合我们需要的类,Doug Lea已经考虑到了这类需求,所以在ExecutorService的基础上,又提供了一个接口——ScheduledExecutorService,该接口也是在JDK1.5时,随着J.U.C引入的,ScheduledExecutorService提供了一系列schedule方法,可以在给定的延迟后执行提交的任务,或者每个指定的周期执行一次提交的任务

<wiz_code_mirror>
 
 
 
 
 
 
 
public interface ScheduledExecutorService extends ExecutorService {
 
    /**
     * 提交一个待执行的任务, 并在给定的延迟后执行该任务.
     *
     * @param command 待执行的任务
     * @param delay   延迟时间
     * @param unit    延迟时间的单位
     */
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
 
    /**
     * 提交一个待执行的任务(具有返回值), 并在给定的延迟后执行该任务.
     *
     * @param command 待执行的任务
     * @param delay   延迟时间
     * @param unit    延迟时间的单位
     * @param <V>     返回值类型
     */
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
 
    /**
     * 提交一个待执行的任务.
     * 该任务在 initialDelay 后开始执行, 然后在 initialDelay+period 后执行, 接着在 initialDelay + 2 * period 后执行, 依此类推.
     *
     * @param command      待执行的任务
     * @param initialDelay 首次执行的延迟时间
     * @param period       连续执行之间的周期
     * @param unit         延迟时间的单位
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
 
    /**
     * 提交一个待执行的任务.
     * 该任务在 initialDelay 后开始执行, 随后在每一次执行终止和下一次执行开始之间都存在给定的延迟.
     * 如果任务的任一执行遇到异常, 就会取消后续执行. 否则, 只能通过执行程序的取消或终止方法来终止该任务.
     *
     * @param command      待执行的任务
     * @param initialDelay 首次执行的延迟时间
     * @param delay        一次执行终止和下一次执行开始之间的延迟
     * @param unit         延迟时间的单位
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
}
 
// 示例
public class ScheduleExecutorTest {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = someScheduler;     // 创建一个ScheduledExecutorService实例
 
        final ScheduledFuture<?> scheduledFuture = scheduler.scheduleAtFixedRate(new BeepTask(), 10, 10,
                TimeUnit.SECONDS);                              // 每隔10s蜂鸣一次
 
        scheduler.schedule(new Runnable() {
            @Override
            public void run() {
                scheduledFuture.cancel(true);
            }
        }, 1, TimeUnit.HOURS)       // 1小时后, 取消蜂鸣任务
    }
 
    private static class BeepTask implements Runnable {
        @Override
        public void run() {
            System.out.println("beep!");
        }
    }
}
 
 

ThreadPoolExecutor

构造函数
  1. 线程池必须手动通过 ThreadPoolExecutor 的构造函数来声明,避免使用Executors 类的 newFixedThreadPool 和 newCachedThreadPool ,因为可能会有 OOM 的风险。
    Executors 返回线程池对象的弊端如下:
    FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
    2 实际使用中需要根据自己机器的性能、业务场景来手动配置线程池的参数比如核心线程数、使用的任务队列、饱和策略等等。
    3 我们应该显示地给我们的线程池命名,这样有助于我们定位问题。
<wiz_code_mirror>
 
 
 
 
 
 
 
    /**
     * 用给定的初始参数创建一个新的ThreadPoolExecutor。
     */
       //假如10个人去银行取钱,此时有两个办公窗口,最大窗口为4,第一个人,第二个人来就直接处理,2个窗口就是核心线程数,还有2个空置窗口,即最大线程数为4,假如来了更多人取钱,此时银行会让这些人等待,等待的座位只有10个,即此时如果阻塞队列满了,会激活空置窗口,即查找空闲线程活激活线程来处理任务。如果队列和窗口都满了,这时候就会激活饱和策略处理。
    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;
    }
 
 
参数说明
<wiz_code_mirror>
 
 
 
 
 
 
 
1、corePoolSize
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于
corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
2、maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。
3、keepAliveTime
线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用。
4、unit
keepAliveTime的单位
5、workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口。
在JDK中提供了如下阻塞队列:
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
priorityBlockingQuene:具有优先级的无界阻塞队列;
6、threadFactory
创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。
7、handler
线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
AbortPolicy:直接抛出异常,默认策略;
CallerRunsPolicy:用调用者所在的线程来执行任务;
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务;
当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
 
 
ThreadPoolExecutor的状态变量
<wiz_code_mirror>
 
 
 
 
 
 
 
// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
RUNNING 111 表示正在运行
SHUTDOWN 000 表示拒绝接收新的任务
STOP 001 表示拒绝接收新的任务并且不再处理任务队列中剩余的任务,并且中断正在执行的任务。
TIDYING 010 表示所有线程已停止,准备执行terminated()方法。
TERMINATED 011 表示已执行完terminated()方法。
 
 
提交任务
<wiz_code_mirror>
 
 
 
 
 
 
 
可以使用 execute() submit() 方法来向线程池中提交任务。
public void execute(Runnable command);
execute() 方法用来提交不需要返回值的任务,无需判断任务是否被线程池执行成功。
其接受参数是一个 Runnable 实例。
 
public <T> Future<T> submit(Runnable task, T result);
public <T> Future<T> submit(Callable<T> task);
public Future<?> submit(Runnable task);
 
submit() 方法用于提交需要返回值的任务。线程池会返回一个 Future 对象,通过其来判断任务是否执行成功。
 
 
Executors类提供了4种不同的线程池

1 newCachedThreadPool
2 newFixedThreadPool
3 newScheduledThreadPool
4 newSingleThreadExecutor

newFixedThreadPool

newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,对于超出的线程会在LinkedBlockingQueue队列中等待。该线程池的线程会维持在指定线程数,不会进行回收。可控制线程最大并发数。

<wiz_code_mirror>
 
 
 
 
 
 
 
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
 
示例:
  ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 9; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}", index);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService.shutdown();
    }
11:58:31.403 [pool-1-thread-1] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:0
11:58:31.403 [pool-1-thread-2] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:1
11:58:31.403 [pool-1-thread-3] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:2
-------------
11:58:32.417 [pool-1-thread-1] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:3
11:58:32.417 [pool-1-thread-3] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:4
11:58:32.417 [pool-1-thread-2] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:5
-------------
11:58:33.417 [pool-1-thread-3] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:6
11:58:33.417 [pool-1-thread-1] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:7
11:58:33.417 [pool-1-thread-2] INFO thread.ThreadSchool.threadPool.ThreadPoolExample2 - task:8
 
 
newCachedThreadPool

创建一个可缓存的无界线程池,该方法无参数。当线程池中的线程空闲时间超过60s则会自动回收该线程,当任务超过线程池的线程数则创建新线程。线程池的大小上限为Integer.MAX_VALUE,可看做是无限大。有空闲线程则复用空闲线程,若无空闲线程则新建线程,一定程序减少频繁创建/销毁线程,减少系统开销。

<wiz_code_mirror>
 
 
 
 
 
 
 
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
示例:
ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}",Thread.currentThread().getName()+"---"+index);
                }
            });
             /*try { 
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            //如果把注释放开会复用空闲线程
            //task:pool-1-thread-1---0
            //task:pool-1-thread-1---1
            //task:pool-1-thread-1---2
            //task:pool-1-thread-1---3
            //task:pool-1-thread-1---4
        }
        executorService.shutdown();
    }
task:pool-1-thread-1---0
task:pool-1-thread-5---4
task:pool-1-thread-3---2
task:pool-1-thread-4---3
task:pool-1-thread-2---1
 
 
newSingleThreadExecutor

有且仅有一个工作线程执行任务,所有任务按照指定顺序执行,即遵循队列的入队出队规则,所有任务都保存队列LinkedBlockingQueue中,等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。

<wiz_code_mirror>
 
 
 
 
 
 
 
public static ExecutorService newSingleThreadExecutor() {
    //线程池中只有一个线程进行任务执行,其他的都放入阻塞队列
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
示例:
 ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}", Thread.currentThread().getName()+"-----"+index);
                }
            });
        }
    }
    task:pool-1-thread-1-----0
    task:pool-1-thread-1-----1
    task:pool-1-thread-1-----2
    task:pool-1-thread-1-----3
    task:pool-1-thread-1-----4
 
 
newScheduledThreadPool

可定时执行或周期执行任务的线程池,该方法可指定线程池的核心线程个数。

<wiz_code_mirror>
 
 
 
 
 
 
 
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
示例:
  ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                log.warn("schedule run");
            }
        }, 3, TimeUnit.SECONDS);//延迟3s
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                log.warn("schedule run");
            }
        }, 1, 3, TimeUnit.SECONDS);//1s后,每3s一次地周期性执行任务
        executorService.shutdown();
 
 
示例
<wiz_code_mirror>
 
 
 
 
 
 
 
 public class ThreadTask implements Runnable {
    private String url;
    private List<String> list;
 
    public ThreadTask(String url, List<String> list) {
        this.url = url;
        this.list = list;
    }
 
    @Override
    public void run() {
        String result = this.send(url);
        list.add(result);
    }
 
    //模拟接口请求
    private String send(String url) {
        try {
            Thread.sleep(1000);
            System.out.println(url + "接口请求中...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String result = "interfaceResult" + new Random().nextInt(100);
        return result;
    }
}
 
public class ThreadTaskForReturn implements Callable {
    private String url;
    private List<String> list;
 
    public ThreadTaskForReturn(String url, List<String> list) {
        this.url = url;
        this.list = list;
    }
 
    @Override
    public String call() throws Exception {
        String result = this.send(url);
        list.add(result);
        return list.size() + "";
    }
 
    private String send(String url) {
            try {
                Thread.sleep(1000);
                System.out.println(url + "接口请求中...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        String result = "interfaceResult" + new Random().nextInt(100);
        return result;
    }
}
 
测试:
public static void main(String[] args) {
        test1();//无返回值,适合一些异步操作
        (比如方法中有数据审核和存储两步,审核方法完毕后,存储可做异步操作,不需要等待)
        test2();//有返回值,可判断是否执行结束
    }
 
    //测试1
    public static void test1() {
        List<String> listUrl = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            listUrl.add("interface" + i);
        }
        List<String> result = Collections.synchronizedList(new ArrayList<String>());
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        if (listUrl != null && listUrl.size() > 0) {
            for (String interfaceUrl : listUrl) {
                String url = interfaceUrl;
                ThreadTask threadTask = new ThreadTask(url, result);
                executorService.execute(threadTask);
            }
        }
        executorService.shutdown();
    }
 
    //测试2
    public static void test2() {
        List<Future<String>> resultList = new ArrayList<Future<String>>();
        //模拟接口url
        List<String> listUrl = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            listUrl.add("interface" + i);
        }
        //存入返回值
        List<String> result = Collections.synchronizedList(new ArrayList<String>());
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        if (listUrl != null && listUrl.size() > 0) {
            for (String interfaceUrl : listUrl) {
                String url = interfaceUrl;
                //创建任务
                ThreadTaskForReturn threadTask = new ThreadTaskForReturn(url, result);
                //执行任务
                Future<String> future = executorService.submit(threadTask);
                resultList.add(future);
            }
            for (Future<String> fs : resultList) {
                try {
                    //while (!fs.isDone()) 
                    //通过GET 获取线程的执行结果,如果没有执行结束,则此get方法会处于等待状态,直到线程执行结束
                    fs.get();
                    /*
                    String i=fs.get();
                    //可以通过返回值判断并返回结果
                    if(i.equals("1")){
                        executorService.shutdown();
                        return i;
                    }
                    */
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                } finally {
                    executorService.shutdown();
                //shutdown()
                //shutdown并不是直接关闭线程池,而是不再接受新的任务...如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池....
                //shutdownNow()
                //这个方法表示不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止...
                }
            }
            System.out.println(result.size());
            //在这里执行存储数据操作
            System.out.println("END");
        }
    }
 
 
posted @ 2022-08-20 16:46  BitBean  阅读(40)  评论(0编辑  收藏  举报