zoukankan      html  css  js  c++  java
  • 线程池ThreadPoolExecutor

     

    一 概述

    1.线程池产生背景

    在多线程环境下,频繁地创建与销毁线程会耗费大量的系统资源,降低运行性能,因此产生了一种设计思想:将创建好的线程放到一个容器中,需要时从容器取得线程,使用完毕将线程归还容器,这样就可以重复利用线程,避免了重复创建与销毁造成的资源消耗,提高了性能。

    2.什么是线程池?

    元素:线程。
    本质:容器。
    设计目的:使得线程能够被重复使用,降低系统消耗。
    一个以多个线程为元素、通过重复使用降低系统消耗的容器。

    二 ThreadPoolExecutor

    该类是线程池思想的核心类,继承关系如图。

    一个代表性的构造方法;

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

    1.coolPoolSize

    核心池大小。这个数字是根据一般情况下并发访问的线程数目设定的。默认情况下,线程池创建完毕后,池中并没有线程,有任务到达以后才开始创建线程。

    2.maximumPoolSize

    线程池中允许出现的最大线程数。维护线程需要消耗系统资源,最大线程数目的设定既考虑高峰时期最大并发访问数量,也兼顾了系统性能。

    3.keepAliveTime

    这个参数针对的是超出核心池大小的多余线程。设定一个时间,如果多余线程空闲时间超过该值,线程会被终止。默认情况下,只有当线程数目大于核心池大小时才有效,如果allowCoreThreadTimeOut设定为true,当线程数目小于核心池大小时也有效。

    4.unit

    keepAliveTime的单位。

    5.workQueue

    一个阻塞队列,用来存储等待执行的任务。BlockingQueue有几个常用的实现类:

    • ArrayBlockingQueue:不常用。
    • LinkedBlockingQueue:常用。
    • SynchronousQueue:常用。

    6.threadFactory

    线程工厂,用来创建线程。

    7.handler

    当线程数目到达最大值时,拒绝处理任务时采取的策略。

    三 执行过程

    1.当线程数目小于核心数目时,每来一个任务就创建一个线程去执行该任务。
    2.当线程数目大于等于核心数目时,首先尝试将新到达的任务添加到任务缓存队列中,若添加成功,等待空闲线程执行该任务,添加失败尝试创建新的线程处理该任务。
    3.如果线程达到最大数目,新任务达到时会采取拒绝处理策略。

    execute()是ThreadPoolExecutor的核心方法,下面对该源码进行分析:

    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            /*
             * Proceed in 3 steps:
             *
             * 1. If fewer than corePoolSize threads are running, try to
             * start a new thread with the given command as its first
             * task.  The call to addWorker atomically checks runState and
             * workerCount, and so prevents false alarms that would add
             * threads when it shouldn't, by returning false.
             *
             * 2. If a task can be successfully queued, then we still need
             * to double-check whether we should have added a thread
             * (because existing ones died since last checking) or that
             * the pool shut down since entry into this method. So we
             * recheck state and if necessary roll back the enqueuing if
             * stopped, or start a new thread if there are none.
             *
             * 3. If we cannot queue task, then we try to add a new
             * thread.  If it fails, we know we are shut down or saturated
             * and so reject the task.
             */
            int c = ctl.get();
           //workerCountOf(c)用于获取线程池中当前线程数目
            if (workerCountOf(c) < corePoolSize) {
                //addWorker(command,true)创建新的线程执行该任务
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            //当线程数目大于等于corePoolSize时,首先尝试将任务添加到任务缓存队列中
            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(Runnable firstTask, boolean core)源码:

    private boolean addWorker(Runnable firstTask, boolean core) {
            retry:
            for (;;) {
               ........................................................................
    
                for (;;) {
                    int wc = workerCountOf(c);
                   //当core=true时,如果线程数目小于corePoolSize,程序继续执行,创建新线程;当core=false时,如果线程数目大于等于
                   //maximumPoolSize,程序终止执行,返回false,不会创建新的线程。
                    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;
                    // else CAS failed due to workerCount change; retry inner loop
                }
            }
    
            boolean workerStarted = false;
            boolean workerAdded = false;
            Worker w = null;
            try {
                //为任务创建一个新的线程
                w = new Worker(firstTask);
                ..............................................
            } finally {
                if (! workerStarted)
                    addWorkerFailed(w);
            }
            return workerStarted;
        }

    四 使用

    通常不直接使用ThreadPoolExecutor的构造方法来创建对象,而是使用Executors的静态方法来创建对象。

    newCachedThreadPool();

    corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,使用SynchronousQueue作为任务缓存队列,如果线程池中没有可用的线程,每来一个任务创建一个线程。

    newFixedThreadPool(int nThreads);

    corePoolSize=nThreads,maximumPoolSize=nThreads,keepAliveTime=0s,使用LinkedBlockingQueue作为任务缓存队列。
    线程池执行对象创创建完毕以后,将任务通过execute(Runnable command)方法提交给线程池执行对象即可。

    参考:

    http://www.cnblogs.com/dolphin0520/p/3932921.html

  • 相关阅读:
    android中listview分页加载数据
    android listview的HeadView左右切换图片(仿新浪,网易,百度等切换图片)
    Opencv cvCircle函数
    我是怎样成长为系统架构师的
    C++游戏编程(一开篇)
    cidaemon.exe进程cpu占用率高及关闭cidaemon.exe进程方法
    curl命令具体解释
    HDU 4334 Trouble
    美国地名大全(美国城市名称英文、中文)
    几种常见模式识别算法整理和总结
  • 原文地址:https://www.cnblogs.com/tonghun/p/7086342.html
Copyright © 2011-2022 走看看