线程池

人走茶凉 提交于 2020-02-14 23:25:04
1. 线程池

线程池是一种复用线程的技术,由于线程的销毁和创建会大大浪费时间以及浪费内存,而线程池可以让线程执行完任务后不立即销毁,让线程重复使用,继续执行其他任务。

2. 线程池详解
ThreadPoolExecutor

线程池中最核心的类是ThreadPoolExecutor,它实现了execute(Runnable command) ,这个方法是用来向线程池传入任务。此类的构造函数如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

参数含义如下:

  • corePoolSize:核心线程池的大小,如果核心线程池有空闲位置,新的任务就会被核心线程池新建一个线程执行
  • maximunPoolSize:线程池能创建最大的线程数量。如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize,否则会采取拒绝接受任务策略
  • keepAliveTime:非核心线程能够空闲的最长时间,如果设置了allowCoreThreadTimeOut = true,也表示核心线程能够空闲的最长时间。超过时间,线程终止。
  • unit:时间单位,和keepAliveTime配合使用。
  • workQueue:缓存队列,用来存放等待被执行的任务。

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

  • threadFactory:线程工厂,用来创建线程,需要自己实现,若不传入则使用Executor.Executors.defaultThreadFactory() 默认工厂
  • handler:拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略,若不传入则使用默认拒绝策略AbortPolicy

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

Executors

Executors类中提供的几个静态方法来创建线程池

Executors.newCachedThreadPool(); //支持的线程数可动态扩展,缓冲池容量大小为Integer.MAX_VALUE,线程空闲超过60s,就销毁回收
Executors.newSingleThreadExecutor(); //只支持1个核心线程
Executors.newFixedThreadPool(int); //只支持n个是核心线程

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
3. 线程池原理概述

在这里插入图片描述

任务提交后的流程分析

用户通过submit提交一个任务。线程池会执行如下流程:

1.判断当前运行的worker数量是否超过corePoolSize,如果不超过corePoolSize。就创建一个worker直接执行该任务,执行完该任务之后再缓存起来。—— 线程池最开始是没有worker在运行的
2. 如果正在运行的worker数量超过或者等于corePoolSize,那么就将该任务加入到workQueue队列中去。
3. 如果workQueue队列满了,也就是offer方法返回false的话,就检查当前运行的worker数量是否小于maximumPoolSize,如果小于就创建一个worker直接执行该任务。
4. 如果当前运行的worker数量是否大于等于maximumPoolSize,那么就执行RejectedExecutionHandler来拒绝这个任务的提交。
5. 当一个线程执行完了自己的任务,他还会尝试从队列中取任务继续执行。若队列中无任务,可能该线程会阻塞并等待任务到来,可能会等待一段时间后被销毁。
4. 源码解析
ThreadPoolExecutor关键属性

//一个原子性变量,保存线程池的状态以及work线程数量
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;

/* runState is stored in the high-order bits
* RUNNING:  Accept new tasks and process queued tasks*   
* SHUTDOWN: Don't accept new tasks, but process queued tasks*   
* STOP:  Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
* TIDYING:  All tasks have terminated, workerCount is zero, the thread transitioning to state  TIDYING will run the terminated() hook method 
* TERMINATED: terminated() has completed
*/

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;


//存放任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;

//worker的集合,用set来存放,用于缓存线程
private final HashSet<Worker> workers = new HashSet<Worker>();

//当队列满了并且worker的数量达到maxSize的时候,执行具体的拒绝策略
private volatile RejectedExecutionHandler handler;

// 默认拒绝策略
private static final RejectedExecutionHandler defaultHandler =    new AbortPolicy();

//超出coreSize的worker的生存时间
private volatile long keepAliveTime;

//核心worker的数量
private volatile int corePoolSize;

//最大worker的数量, 当workQueue满了才会用到这个参数
private volatile int maximumPoolSize;

// 创建线程的工厂,若不传入则使用Executor.Executors.defaultThreadFactory() 
private volatile ThreadFactory threadFactory;

// 用于访问work set的锁,虽然可以用并发set,但是锁可以避免一些不必要的中断问题尤其在shutdown期间
private final ReentrantLock mainLock = new ReentrantLock();


ThreadPoolExecutor的execute方法:
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //workerCountOf(c)会获取当前正在运行的worker数量
        if (workerCountOf(c) < corePoolSize) {
            //如果workerCount小于corePoolSize,就创建一个worker然后直接执行该任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //isRunning(c)是判断线程池是否在运行中,如果线程池被关闭了就不会再接受任务
        //后面将任务加入到队列中
        if (isRunning(c) && workQueue.offer(command)) {
            //如果添加到队列成功了,会再检查一次线程池的状态
            int recheck = ctl.get();
            //如果线程池关闭了,就将刚才添加的任务从队列中移除
            if (! isRunning(recheck) && remove(command))
                //执行拒绝策略
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果加入队列失败,就尝试直接创建worker来执行任务
        else if (!addWorker(command, false))
            //如果创建worker失败,就执行拒绝策略
            reject(command);
}


private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        //使用自旋+cas失败重试来保证线程竞争问题
        for (;;) {
            //先获取线程池的状态
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果线程池是关闭的,或者workQueue队列非空,就直接返回false,不做任何处理
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //根据入参core 来判断可以创建的worker数量是否达到上限,如果达到上限了就拒绝创建worker
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //没有的话就尝试修改ctl添加workerCount的值。这里用了cas操作,如果失败了下一个循环会继续重试,直到设置成功
                if (compareAndIncrementWorkerCount(c))
                    //如果设置成功了就跳出外层的那个for循环
                    break retry;
                //重读一次ctl,判断如果线程池的状态改变了,会再重新循环一次
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            //创建一个worker,将提交上来的任务直接交给worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                //加锁,防止竞争
                mainLock.lock();
                try {
                    int c = ctl.get();
                    int rs = runStateOf(c);
                    //还是判断线程池的状态
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //如果worker的线程已经启动了,会抛出异常
                        if (t.isAlive()) 
                              throw new IllegalThreadStateException();
                        //添加新建的worker到线程池中
                        workers.add(w);
                        int s = workers.size();
                        //更新历史worker数量的最大值
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        //设置新增标志位
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //如果worker是新增的,就启动该线程
                if (workerAdded) {
                    t.start();
                     //成功启动了线程,设置对应的标志位
                    workerStarted = true;
                }
            }
        } finally {
            //如果启动失败了,会触发执行相应的方法
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
}
ThreadPoolExecutor的runWorker方法
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //没执行任务时,空闲状态,允许中断自己
        w.unlock();
        boolean completedAbruptly = true;
        try {
            // 不断执行任务,先执行一开始给work线程的初始任务,在不断从任务队列中取任务来执行,直到队列中没有任务为止。当task == null时,就要把该线程清除了
            while (task != null || (task = getTask()) != null) {
                
                 //执行任务前先加锁,主要为了避免被shutdown方法中断,shutdown方法里面会尝试给这个线程加锁,如果这个线程在执行,就无法获取锁,就不会中断它。
                w.lock();
                
             //判断线程池状态,如果线程池被强制关闭了,就不执行该任务了,马上中断。
                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;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
           // 回收线程
            processWorkerExit(w, completedAbruptly);
        }
    }
    

根据getTask的返回值,一种情况是队列不为空,直接取出任务返回,第二种是队列为空,但是该worker线程阻塞,等待任务队列添加任务,第三种情况是队列为空,返回null,清理掉该worker线程

    private Runnable getTask() {
        boolean timedOut = false; 

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果线程池已经关闭了,就直接返回null,
            //如果这里返回null,调用的那个worker就会跳出while循环,然后执行完销毁线程
            //SHUTDOWN状态表示执行了shutdown()方法
            //STOP表示执行了shutdownNow()方法
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            //获取当前正在运行中的worker数量
            int wc = workerCountOf(c);

            // 表示是否可能销毁线程能够,如果设置了核心worker也会超时或者当前正在运行的worker数量超过了corePoolSize,就可能要销毁线程了
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
            //当可能销毁线程条件和队列无任务条件满足时,表示该线程空闲,需要进行销毁
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                //通过cas来设置WorkerCount,如果多个线程竞争,只有一个可以设置成功
                //最后如果没设置成功,就进入下一次循环,说不定下一次worker的数量就没有超过corePoolSize了,也就不用销毁worker了
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //过了这个keepAliveTime时间还没有任务进队列就会返回null,那worker就会销毁
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    // worker线程阻塞式取元素
                    workQueue.take();
                if (r != null)
                    return r;
                //如果r为null,就设置timedOut为true
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
}

    
private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly)
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }
shutdown()和shutdownNow()源码解析

shutdown方法会将线程池的状态设置为SHUTDOWN,线程池进入这个状态后,就拒绝再接受任务,将所有空闲线程中断,然后会将剩余的任务全部执行完。

shutdownNow做的比较绝,它先将线程池状态设置为STOP,然后拒绝所有提交的任务。最后中断正在运行中的worker,然后清空任务队列。

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //检查是否可以关闭线程
            checkShutdownAccess();
            //设置线程池状态
            advanceRunState(SHUTDOWN);
            //尝试中断所有空闲worker
            interruptIdleWorkers();
             //预留方法,留给子类实现
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
}

private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
}

private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //遍历所有的worker
            for (Worker w : workers) {
                Thread t = w.thread;
                //先尝试调用w.tryLock(),如果获取到锁,就说明worker是空闲的,就可以直接中断它
                //注意的是,worker自己本身实现了AQS同步框架,然后实现的类似锁的功能
                //它实现的锁是不可重入的,所以如果worker在执行任务的时候,会先进行加锁,这里tryLock()就会返回false
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
}
public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //检测权限
            advanceRunState(STOP);
            //中断所有的worker
            interruptWorkers();
            //清空任务队列
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
}

private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //遍历所有worker,然后调用中断方法
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
}
5. 线程池的使用示例
public class Task implements Runnable{
      private int num;
      public Task(int num) {
          this.num=num;
      }
    @Override
    public void run() {
        System.out.println("正在执行任务  "+num);
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程"+num+"执行完毕");
    }
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5));
        for (int i=1; i<=15; i++) {
            Task task = new Task(i);
            System.out.println("线程池中线程 数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                    executor.getQueue().size());
            executor.submit(task);
        }
        executor.shutdown();
    }
}
线程池中线程 数目:0,队列中等待执行的任务数目:0
线程池中线程 数目:1,队列中等待执行的任务数目:0
线程池中线程 数目:2,队列中等待执行的任务数目:0
线程池中线程 数目:3,队列中等待执行的任务数目:0
线程1 正在执行
线程2 正在执行
线程池中线程 数目:4,队列中等待执行的任务数目:0
线程3 正在执行
线程池中线程 数目:5,队列中等待执行的任务数目:0
线程4 正在执行
线程池中线程 数目:5,队列中等待执行的任务数目:1
线程5 正在执行
线程池中线程 数目:5,队列中等待执行的任务数目:2
线程池中线程 数目:5,队列中等待执行的任务数目:3
线程池中线程 数目:5,队列中等待执行的任务数目:4
线程池中线程 数目:5,队列中等待执行的任务数目:5
线程池中线程 数目:6,队列中等待执行的任务数目:5
线程池中线程 数目:7,队列中等待执行的任务数目:5
线程11 正在执行
线程池中线程 数目:8,队列中等待执行的任务数目:5
线程12 正在执行
线程13 正在执行
线程池中线程 数目:9,队列中等待执行的任务数目:5
线程14 正在执行
线程15 正在执行
线程2 执行完毕
线程4 执行完毕
线程1 执行完毕
线程3 执行完毕
线程8 正在执行
线程7 正在执行
线程6 正在执行
线程9 正在执行
线程12 执行完毕
线程5 执行完毕
线程11 执行完毕
线程13 执行完毕
线程10 正在执行
线程15 执行完毕
线程14 执行完毕
线程7 执行完毕
线程6 执行完毕
线程8 执行完毕
线程9 执行完毕
线程10 执行完毕

可以看出来,任务执行是一个异步过程,线程调度由线程池完成,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面当任务缓存队列满了之后,便创建新的线程,当超过15个线程时,就会执行拒绝策略。

参考文章:
https://blog.csdn.net/u013332124/article/details/79587436

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!