线程池框架Executor(并发编程篇)

目录

为什么引入Executor线程池框架

Executor原理

Executor生命周期

Executor框架介绍与使用

ThreadPoolExecutor 线程池类

ExecutorService接口

Executor接口

ScheduledExecutorService接口


为什么引入Executor线程池框架

① 重用存在的线程,减少对象创建、消亡的开销,提高性能;

② 线程的创建和运行分开,达到解耦目的;

③ 可有效控制最大并发线程数,提高系统资源的使用率;

Executor原理


 

Executor生命周期

  • RUNNING:可以接收新任务,并且处理阻塞队列中的任务
  • SHUTDOWN:关闭状态,不能接收新任务,可以继续处理阻塞队列中的任务。在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。
  • STOP:不能接收新任务,也不能处理阻塞队列中的任务,会中断正在处理的任务。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态。
  • TIDYING:所有任务都执行完成,线程池中workerCount (有效线程数) 为0。线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。
  • TERMINATED:调用terminated()方法进入该状态。

Executor框架介绍与使用

说明:

  • Executor 执行器接口,该接口定义执行Runnable任务的方式。
  • ExecutorService 该接口定义提供对Executor的服务。
  • ScheduledExecutorService 定时调度接口。
  • AbstractExecutorService 执行框架抽象类。
  • ThreadPoolExecutor JDK中线程池的具体实现。
  • Executors 线程池工厂类。

Executor将任务的提交过程与执行过程解耦,并用Runnable来表示任务。执行的任务放入run方法中即可,将Runnable接口的实现类交给线程池的execute方法,作为它的一个参数。如果需要给任务传递参数,可以通过创建一个Runnable接口的实现类来完成。

Executor可以支持多种不同类型的任务执行策略。

Executor基于生产者消费者模式,提交任务的操作相当于生产者,执行任务的线程则相当于消费者。

ThreadPoolExecutor 线程池类

线程池是一个复杂的任务调度工具,它涉及到任务、线程池等的生命周期问题。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的。

JDK中的线程池均由ThreadPoolExecutor类实现。其构造方法如下:

corePoolSize(线程池的基本大小):当提交一个新的任务给线程池后,会创建一个新的线程来执行任务(当前线程池线程数 < corePoolSize),等到需要执行的任务数大于线程池基本大小大于corePoolSize时就不再创建,而是把任务放进保持的等待队列

maximumPoolSize(线程池最大大小):线程池允许最大线程数如果阻塞队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行。因为线程池执行任务时是线程池基本大小满了,后续任务进入阻塞队列,阻塞队列满了,在创建线程。

keepAliveTime(线程活动保持时间):空闲的线程保持活动得时间(好像只有在newCachedThreadPool这个方法的参数中才有意义(不为0))

TimeUnit(线程活动保持时间的单位):

        TimeUnit.DAYS; //天
        TimeUnit.HOURS; //小时
        TimeUnit.MINUTES; //分钟
        TimeUnit.SECONDS; //秒
        TimeUnit.MILLISECONDS; //毫秒
        TimeUnit.MICROSECONDS; //微妙
        TimeUnit.NANOSECONDS; //纳秒

workQueue(工作队列):用于保存待执行的任务的阻塞队列,有如下几种

  •  ArrayBlockingQueue:数组结构的有界阻塞队列,先进先出FIFO
  •  LinkedBlockingQueue:链表结构的无界阻塞队列。先进先出FIFO排序元素,静态方法Executors.newFixedThreadPool和Executors.newFixedThreadPool.newSingleThreadExecutor使用这个队列
  •  SynchronousQueue:不存储元素的阻塞队列,就是每次插入操作必须等到另一个线程调用移除操作,静态方法Executors.newCachedThreadPool使用这个方法

threadFactory:设置创建线程的工厂,通过这个工厂可以给线程一些比较有意义的名字

handler:一种饱和策略,当线程池和队列都饱满时拒绝处理任务的策略,默认是AbortPolicy,表示无法处理新的任务时抛出异常;拒绝策略有如下几种:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常

  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

 

ThreadPoolExecutor需要注意以下概念:

  • 若线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

  • 若线程池中的线程数量等于 corePoolSize且缓冲队列 workQueue未满,则任务被放入缓冲队列。

  • 若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量小于maximumPoolSize,则建新的线程来处理被添加的任务。

  • 若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

  • 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。

ExecutorService接口

线程池接口。ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:

Future<?> submit(Runnable task)

<T> Future<T> submit(Callable<T> task)

 

这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。

在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。

Executor接口

Executor是一个线程执行接口。任务执行的主要抽象不是Thead,而是Executor。

public interface Executor{
    void executor(Runnable command);
}

 

Executor将任务的提交过程与执行过程解耦,并用Runnable来表示任务。执行的任务放入run方法中即可,将Runnable接口的实现类交给线程池的execute方法,作为它的一个参数。如果需要给任务传递参数,可以通过创建一个Runnable接口的实现类来完成。

Executor可以支持多种不同类型的任务执行策略。

Executor基于生产者消费者模式,提交任务的操作相当于生产者,执行任务的线程则相当于消费者。

ScheduledExecutorService接口

ScheduledExecutorService描述的功能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。这包括延迟时间一次性执行、延迟时间周期性执行以及固定延迟时间周期性执行等。当然了继承ExecutorService的ScheduledExecutorService拥有ExecutorService的全部特性。

①:Java类库可以通过调用Executors的静态工厂方法来创建线程

public class Executors extends Object

static ExecutorService newCachedThreadPool()

创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。

static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。

static ExecutorService newFixedThreadPool(int nThreads)

创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。

static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

创建一个线程池,重用固定数量的线程,从共享无界队列中运行,使用提供的ThreadFactory在需要时创建新线程。

static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。

static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)

创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。

②:public interface ExecutorService  extends Executor;

boolean isShutdown()

如果此执行者已关闭,则返回 true 。

boolean isTerminated()

如果所有任务在关闭后完成,则返回 true 。

void shutdown()

启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。

List<Runnable> shutdownNow()

尝试停止所有主动执行的任务,停止等待任务的处理,并返回正在等待执行的任务列表。

<T> Future<T> submit(Callable<T> task)

提交值返回任务以执行,并返回代表任务待处理结果的Future。

  public static void main(String[] args) {
        //固定长度的线程池
        //ExecutorService pool = Executors.newFixedThreadPool(5);
        //将创建一个缓存线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        //可调度的线程池
        //ExecutorService pool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            pool.execute(new ThreadDemo1());
        }
        //停止
        pool.shutdown();
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章