JUC--线程池解析

  1. ThreadPoolExecutor继承结构


    图片.png

    1.1 构造方法:七个参数的分别代表corePoolSize 核心线程数,maximumPoolSize,最大线程数,keepAliveTime 存活时间(也就是线程池中线程可以空闲的时间),unit存活时间的单位,workQueue阻塞队列,threadFactory线程工厂,handler拒绝策略.

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

1.2 线程池提交任务的方式有两种
public void execute(Runnable command),该方法是由Excutor顶层接口提供的
public Future<?> submit(Runnable task) ,该方法是由ExecutorService扩展接口提供。
由返回值类型可以看出一个可以异步的获取返回值结果。

  1. submit方法();该方法将task包装成一个RunnableFuture对象然后调用execute方法执行。
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
  1. execute()方法
    判断任务是否为空 如果是口则抛出空指针异常。
    然后获取当前线程池中线程的数量(线程池中的数量是通过成员变量原子整形变量ctl记录,分别用高3位记录线程池的状态和和低29位记录线程池中线程的个数)

线程池的状态
2.1. RUNNING,处于正常运行状态接受任务处理任务
2.2. SHUTDOWN,调用shutdown()方法后进入此状态,表示不在接受新任务,处理阻塞队列中的任务
2.3. STOP,调用shutdownNow()方法后进入此状态不接受新任务,不处理已添加的任,并且会中断正在处理的任务。
2.4. TIDYING, 当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态,当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
2.5. TERMINATED,线程池彻底终止,就变成TERMINATED状态。

进入具体的执行流程:
判断当前线程池中的线程数量是否小于核心线程数,如果小于则将创建工作线程Woker执行任务,否则继续进行判断当前线程池是否处于运行状态,如果运行则将当前任务加入阻塞队列(这里注意,如果阻塞队列是无界的则最大线程数也没有意义了因为会一直往队列里添加任务)进行等待,否则进行添加非核心线程addWorker(command, false)fasle表示是非核心线程,具体逻辑在addWorker方法实现

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        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);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker()方法
core表示所添加的任务是否是核心线程
首先进入第一个for(;;)进入执行进行状态检查,然后进入第二个for(;;)使用CAS的方式将线程池中线程数量加1 , wc >= (core ? corePoolSize : maximumPoolSize))这句判断为区分核心线程数与非核心线程数的依据。
接下来进入第二个大逻辑块,先将当前线程包装为Worker对象(Woker为继承CAS类与实现runnable接口),获取到当前Worker的真正的处理线程,如果不为Null则获取全局锁进行添加工作线程,进入同步代码块判断线程池状态 wokers为当前线程池中所有工作线程池集合是一个HashSet,将工作线程添加到HashSet之后进行解锁,然后启动工作线程进入runWorker方法()

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker类的runWorker方法(),首先进行解锁,这时候如果调用shutdown方法的话是允许被中断的,然后进入while循环判断当前任务是否为空如果为空则从任务队列中取元素,获取Woker上的锁,获取到之后进入任务的具体执行如果调用shutdown方法的话是不允许被中断的,判断线程池状态如果是stop则中断当前线程,否则执行任务的具体流程,执行完毕之后将task置空,将当前工作线程处理的任务数加一,解锁,继续调用gettask方法执行取任务执行任务的流程,退出while循环的条件为没有取到任务则退出completedAbruptly代表是抛出异常退出(true)还是正常退出(fasle)

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                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()方法
timeOut代表是否获取超时
进入循环进行从任务队列中取任务,首先判断当前线程池的状态根据其他代码逻辑可知,如果处于shutdown状态并且任务队列为空的情况下则将当前工作线程数CAS减1然后return null将没有取代任务,如果处于stop状态则将当前线程池工作线程数减以不管任务队列是否还有任务。
如果线程池处于running状态,则获取当前线程池中线程的数量 然后用变量time标记为当前线程池线程是否设置超时时间或者当前线程数是否大于核心线程数。进行线程池中线程数量逻辑的判断,之后从任务队列里面取任务分为阻塞等待和定时将取到任务返回

 private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            int wc = workerCountOf(c)
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

processWorkerExit方法
为将去除工作线程后的处理工作,判断当前工作线程是否正常退出,如果因为异常退出则将线程数减1,然后获取全局锁,将该工作线程处理的任务数量加到总任务数量之中,从HashSet移除当前工作线程,解锁,尝试停止线程池。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            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()方法在获取执行的时候会先要获取到Woker的锁然后再去工作线程
shutdownnow()则是直接进行中断不需要获取锁

java提供了Executors线程池的工具类


图片.png
  1. 固定线程数量线程池
    该线程池核心线程数与最大线程数数量相等,且工作线程的存活时间为0,阻塞队列使用的是无界阻塞队列基于链表,适用于可以预测线程数量的业务,或者服务器负载较重,对当前线程数量进行控制。
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  1. 单例线程池
    核心线程数与最大线程数都是1 ,其实最大线程数没有意义因为使用的是无界阻塞队列,所有任务都是按顺序执行的
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲空间,如无可回重用时则创建新线程,队列采用的同步队列,只有当一个任务被取走的时候,才能加入下一个任务,类似于生产者消费者模式
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. 定时任务线程池使用延迟队列,可以实现任务的定时执行。
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

5.:创建一个拥有多个任务队列的线程池,可以减少连接数创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行,底层使用forkjoin框架,实现任务窃取

    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容