Executor框架

Executor框架

一、简介

什么是Executor框架呢?

Executor框架说白了就是一个线程池,用于存放控制线程的启动、执行、关闭。
Executor是一个灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。

每次创建线程new Thread()都比较消耗性能、消耗资源。并且这种方式创建线程缺乏管理。

Executor框架包括:各种线程池、Executor、Executors、ExecutorService,CompletionService、Future、Callable等。

在这里插入图片描述Executor:执行器接口,定义runnable任务执行的方式。
ExecutorService:执行器服务接口,定义对Executor的服务。
ScheduledExecutorService:时间调度执行器服务接口,定义时间调度。
AbstractExecutorService:执行器服务抽象类,实现了接口的默认方法。
ThreadPoolExecutor:线程池具体实现。
Executors:线程池的工厂类,创建各种线程池。

二、ThreadPoolExecutor

在Executors中的各种线程池,就是用ThreadPoolExecutor的方式创建的。

常量及属性

线程池之ThreadPoolExecutor状态控制

后29位存放当前线程数,前3位存放运行状态,总共有5种状态,只能用3位以上来表示。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//记录运行状态和当前线程数量
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;//线程的最大数量
//运行状态:可以接受新任务,并执行已经加入任务队列的任务
private static final int RUNNING    = -1 << COUNT_BITS;
//关闭状态:不接受新任务,但仍然执行已经加入任务队列的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
//停止状态:不接受新任务,不执行已经加入任务队列的任务,还中断正在执行的任务
private static final int STOP       =  1 << COUNT_BITS;
//调整回收状态:所有任务被停止,清空所有线程(workerCount=0)进入该状态,然后执行terminated()方法
private static final int TIDYING    =  2 << COUNT_BITS;
//终止状态:terminated()方法执行完成后进入该状态
private static final int TERMINATED =  3 << COUNT_BITS;

//获取线程池运行状态(即取高三位)
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
数据结构

Worker,实现了runnable接口,继承了AQS

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable {
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;//指向运行该任务的线程
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;//我们需要运行的任务,也就是从线程池中传入的。
    /** Per-thread task counter */
    volatile long completedTasks;
	...
}

在ThreadPoolExecutor中用HashSet将Worker封装起来。

构造方法:

构造器参数:
int corePoolSize:核心线程数,如果当前线程数<核心线程数,就会创建一个新线程执行任务,即使线程池中存在空闲线程。如果当前线程数>核心线程数,就会将新任务放入workQueue中,等待线程池任务调度。
int maximumPoolSize:最大线程数,如果workQueue已满,且当前线程数<maximumPoolSize,会创建新的线程执行任务放入线程池中,直到当前线程数=maximumPoolSize。如果是无界的任务队列,这个参数无效。
int keepAliveTime:线程存活的时间。如果当前线程数>核心线程数,超过这个存活时间之后就会销毁。
TimeUtil unit:keepAliveTime的时间单位。
BlockingQueue<Runnable> workQueue:任务的阻塞队列。用于存放任务。
ThreadFactory:用于创建线程的工厂,可以通过工厂设置线程的name、debug和定位时间等。
RejectedExecutionHandler:拒绝策略,如果当前线程数>最大线程数,根据拒绝策略进行处置。

  • AbortPolicy:丢弃任务并抛出RejectedExecutionHandler异常。默认。
  • DiscardPolicy:丢弃任务但是不抛出异常。
  • DiscardOldestPolicy:丢弃队列中最前面的任务,然后尝试重新执行。
  • CallerRunsPolicy:由调用线程处理该任务。
  • 自定义
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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
执行

ThreadPoolExecutor源码分析

public void execute(Runnable command) {
   if (command == null)
        throw new NullPointerException();
    int c = ctl.get();//获取当前线程数和执行状态
    if (workerCountOf(c) < corePoolSize) {//获取当前线程数与corePoolSize作比较
        if (addWorker(command, true))//尝试创建核心线程放入线程池,成功则返回
            return;
        //返回失败,可能是因为当前线程数已经达到corePoolSize,或者由于线程池状态
        c = ctl.get();//更新执行状态
    }
    if (isRunning(c) && workQueue.offer(command)) {//如果执行状态是Running,就将任务加入队列
        int recheck = ctl.get();//更新执行状态
        if (! isRunning(recheck) && remove(command))//如果执行状态不是Running,就删除任务并调用拒绝策略
            reject(command);
        else if (workerCountOf(recheck) == 0)//如果当前运行线程数为0
            addWorker(null, false);//创建非核心线程
    }
    else if (!addWorker(command, false))//尝试向线程池中添加一个非核心线程执行command
        reject(command);//运行状态原因或队列满了,失败则调用拒绝策略
}
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);//获取运行状态
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&//非运行状态
            ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            //这个条件可替换为(rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
            //也就是说 执行状态为SHUTDOWN 且 执行任务为null 且 队列非空的时候 返回false。所以SHUTDOWN,不接受新任务,但是会执行队列中已有的任务。
            return false;
        for (;;) {
            int wc = workerCountOf(c);//获取当前线程数
            if (wc >= CAPACITY ||//如果线程数大于等于最大容量2^29-1 在这里控制线程数大小
                wc >= (core ? corePoolSize : maximumPoolSize))
                //core为false表示创建非核心线程,wc>=maximumPoolSize,不会创建非核心线程
                //core为true表示创建核心线程,wc>=corePoolSize,不会创建核心线程
                return false;
            if (compareAndIncrementWorkerCount(c))//用cas方法将线程数+1
                break retry;//成功后跳出最外层循环
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)//如果运行状态发生了改变,继续进入外层循环
                continue retry;
            //否则继续内层循环,重试尝试cas,这里用了自旋锁
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    //上面添加线程数量成功,开始添加线程
    boolean workerStarted = false;//线程启动标志位
    boolean workerAdded = false;//线程是否加入workers标志位
    Worker w = null;
    try {
        w = new Worker(firstTask);//新建一个worker
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 获取锁之后重新检查运行状态,可能在上一个获取到锁处理的线程改变了执行状态
                // 比如线程工厂创建失败,线程池被shutdown
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||//如果执行状态是running
                	//或者执行状态是SHUTDOWN且没有新任务
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // 如果这个工作线程已经启动
                        throw new IllegalThreadStateException();
                    workers.add(w); // 将这个工作线程加入线程池
                    int s = workers.size();
                    if (s > largestPoolSize)//如果线程池里的线程数大于线程数峰值
                        largestPoolSize = s;//更新线程数峰值
                    workerAdded = true;//添加线程成功,设置标志位
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {//如果添加成功
                t.start();//启动线程,执行worker里面的thread的run方法
                workerStarted = true;//设置启动标志位
            }
        }
    } finally {
        if (! workerStarted)//如果没有启动成功
            addWorkerFailed(w);//说明添加线程失败
    }
    return workerStarted;
}
//worker类的构造器以及run
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);//Worker类实现了Runnable
    //这里创建的thread执行的任务是worker自己
}
public void run() {
    runWorker(this);
}
//线程池的线程复用
//将队列中的任务不断取出,调用run方法进行执行。而.start()只调用一次
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();//当前运行的线程就是调用线程
    Runnable task = w.firstTask;//获取worker的任务
    w.firstTask = null;
    w.unlock(); // 允许被interrupt
    boolean completedAbruptly = true;
    try {
    //循环知道task==null(线程池关闭、超时等) getTask()从阻塞队列中获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();//执行任务前上锁
            //stop以上状态:不接受新任务,不执行队列中的任务,中断执行中的任务
            if ((runStateAtLeast(ctl.get(), STOP) || 
            (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) 
            && !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);//在执行之前做的事,由子类实现
                Throwable thrown = null;
                try {
                    task.run();//执行任务
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);//在执行之前做的事,由子类实现
                }
            } finally {
                task = null;//将task置空,执行下一个任务
                w.completedTasks++;//完成的任务数加1
                w.unlock();
            }
        }
        completedAbruptly = false;//说明不是用户异常引起
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}
//从阻塞队列中获取任务
private Runnable getTask() {
    boolean timedOut = false; //取任务是否超时
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 如果执行状态大于等于SHUTDOWN状态,并且阻塞队列为空 或者 如果执行状态大于STOP状态
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();//工作线程数减一,因为在启动线程之前已经加一了
            return null;//线程将会退出
        }
        int wc = workerCountOf(c);
        // allowCoreThreadTimeOut 是否允许核心线程超时
        // wc>corePoolSize 
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        // 如果工作线程数大于最大线程数,或者 允许超时并且确实超时了
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {//并且工作线程数大于1 或者 阻塞队列为空
            if (compareAndDecrementWorkerCount(c))//cas进行减一
                return null;//线程将会退出
            continue;//上面线程数减一失败,说明线程数量已被抢先改变,继续循环,
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
                //移除并返回队首元素
            if (r != null)
                return r;
            timedOut = true;//如果取出为null,说明取任务超时
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
执行监控

我们需要覆盖这三种方法做具体实现。

protected void beforeExecute(Thread t, Runnable r) { }//执行之前
protected void afterExecute(Runnable r, Throwable t) { }//执行之后
protected void terminated() { }//退出之前
线程池的关闭
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();//对线程检查是否有权限修改
        advanceRunState(SHUTDOWN);//改变线程池状态位SHUTDOWN
        interruptIdleWorkers();//中断所有线程
        onShutdown(); //留给子类实现,在shutdown做的一些事情 比如ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和区别

各种线程池

2.1 newFixedThreadPool

每次提交一个任务就创建一个线程放入线程池,直到线程达到最大线程数。然后会保持不变。如果某个线程因为执行异常而结束,就会线程池就会补充一个新线程。
创建固定大小的线程池,如果任务数目大于最大线程数,那么没有执行的任务必须等到有任务完成为止。

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

2.2 newSingleThreadExecutor

只有一个线程,相当于一个线程串行执行所有任务。如果该线程出现异常,就会有新的线程来代替。执行顺序即提交顺序。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

2.3 newCachedThreadPool

可缓存的线程池
如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲线程(60s内不执行任务),当任务数增加时,此线程池又可以智能的添加新线程来执行任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
自动扩展线程池的大小,核心线程数为0,意味着每次都是向workQueue中放。

等学了JUC中的集合再来补

SynchronousQueue

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

2.4 newScheduledThreadPool

定时任务调度的线程池。
创建一个大小无限的线程池。定时或周期性执行任务。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

2.5 newSingleThreadScheduledExecutor

单线程定时任务调度

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

三、线程池不要用Executors创建

在阿里巴巴开发手册中指出:
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors各个方法的弊端:

  1. FixedThreadPool和SingleThreadExecutor
    主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM
    这是因为使用LinkedBlockingQueue的无惨构造器使得阻塞队列的容量为Integer.MAX_VALUE
  2. CachedThreadPool 和 ScheduledThreadPool
    主要问题是线程数最多数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM
    maximumPoolSize为Integer.MAX_VALUE

线程池创建的方式:

  1. 引入commons-lang3包
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
  1. 引入com.google.guava包
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("demo-pool-%d").build();
//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown
  1. spring配置线程池方式:自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean
<bean id="userThreadPool"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="100" />
    <property name="queueCapacity" value="2000" />
	<property name="threadFactory" value=threadFactory>
    	<property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>

ThreadPoolExecutor:线程池不允许使用Executors创建

四、AbstractExecutorService

AbstractExecutorService是一个抽象类实现了ExecutorService接口。我们可以在看一下ThreadPoolExecutor,继承了这个抽象类。

能力不高水平有限,在文章中大部分借鉴了其他博客的内容,以及很多未能解决的问题,请大家多多包涵。侵删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值