为了账号安全,请及时绑定邮箱和手机立即绑定

Java并发编程之线程池

标签:
Java JVM 源码

                          

https://img1.sycdn.imooc.com//5d0fa3320001ca9010240768.jpg

                                                         线程池

一、使用线程池的优势:

1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗;   

2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能执行;     

3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,不是线程越多越好。使用线程池可以进行统一的分配,调优和监控。     


二、线程池的参数说明:

1. corePoolSize: 线程池的基本大小,当提交一个任务到线程池时,线程池会创建一个线程来执行,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。    


2. runnableTaskQueue: 任务队列,用于保存等待执行的任务的阻塞队列,可以选择以下几个阻塞队列:      

* ArrayBlockingQueue: 是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序;

* LinkedBlockingQueue: 是一个基于链表结构的阻塞队列,此队列按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool()使用了这个队列;     

* SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列;   

* PriorityBlockingQueue: 一个具有优先级的无限阻塞队列;       


3. maximumPoolSize: 线程池最大大小,线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果;  


4. ThreadFactory: 用于创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题的时候比较方便;   


5. RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一个策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常;其他策略如下:     

* CallerRunsPolicy: 只用调用者所在线程来运行任务;    

* DiscardOldestPolicy: 丢弃队列里最近的一个任务,并执行当前任务;    

* DiscardPolicy: 不处理,丢弃掉;   

* 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略,如记录日志或持久化不能处理的任务;    


6. keepAliveTime: 线程活动保持时间,线程池的工作线程空闲后,保持存活的时间,所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率;    


7. TimeUnit: 线程活动保持时间的单位,可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS,千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒);      


8. 其他参数说明:        

    //线程池的控制状态:用来表示线程池的运行状态(整型的高3位)和运行的worker数量(低29位)            

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));       

    // 29位的偏移量       

    private static final int COUNT_BITS = Integer.SIZE - 3;       

    //最大容量(2^29-1)         

    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;       


    /** 线程运行状态,总共有5个状态,需要3位来表示(所以偏移量为29=32-3)      

    * RUNNING: 接收新任务并且处理已经进入阻塞队列的任务;       

    * SHUTDOWN: 不接受新任务,但是处理已经进入阻塞队列的任务;     

    * STOP: 不接受新任务,不处理已经进入阻塞队列的任务并且中断正在运行的任务;         

    * TIDYING: 所有的任务都已经终止, workerCount为0,线程转化为TIDYING状态并且调用terminated钩子函数;          

    * TERMINATED: terminated钩子函数已经运行完成;       

    */         

    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;      


    //可重入锁      

    private final ReentrantLock mainLock = new ReentrantLock();      


    //存放工作线程集合      

    private final HashSet<Worker> workers = new HashSet<Worker>();         


    //终止条件    

    private final Condition termination = mainLock.newCondition();          


    //最大线程池容量      

    private int largestPoolSize;      


    //已完成任务数量     

    private long completedTaskCount;     


    //线程工厂       

    private volatile ThreadFactory threadFactory;     

 

    //拒绝执行处理器     

    private volatile RejectedExecutionHandler handler;     


    //线程等待运行时间     

    private volatile long keepAliveTime;      


    //是否运行核心线程超时     

    private volatile boolean allowCoreThreadTimeOut;       


    //核心池的大小     

    private volatile int corePoolSize;      


    //最大线程池的大小      

    private volatile int maximumPoolSize;    


    //默认拒绝执行处理器     

    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();      


    //shutdown和shutdownNow的调用者所需的权限     

    private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");   

    

三、线程池的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||                                  //核心大小不能小于0   
        maximumPoolSize <= 0 ||                              //线程池的初始最大容量不能小于0
        maximumPoolSize < corePoolSize ||                    //初始最大容量不能小于核心大小
        keepAliveTime < 0)                                   //keepAliveTime不能小于0
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    //初始化相应的域      
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}


四、线程池的重要方法

(1)提交任务:    

步骤:    

1. 如果运行的线程小于corePoolSize,则尝试使用用户定义的Runnable对象创建一个新的线程,调用addWorker函数会原子性的检查runState和workCount,通过返回false来防止在不应该添加线程时添加了线程;       

2. 如果一个任务能够成功入队,在添加一个线程时任然需要进行双重检查(因为在前一次检查后,该线程死亡了),或者当进入到此方法时,线程池已经shutdown了,所以需要再次检查状态,若有必要,当停止时还需要回滚入队列操作,或者当线程池没有线程时需要创建一个新线程;      

3. 如果无法入队列,那么需要增加一个新的线程,如果此操作失败,那么就意味着线程池已经shutdown或者已经饱和了,所以拒绝任务;     

源码如下:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //获取线程池控制状态
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {  //worker数量小于corePoolSize
            if (addWorker(command, true))   //添加worker
                return;    //成功则返回
            //不成功则再次获取线程池控制状态
            c = ctl.get();
        }
        //线程池处于RUNNING状态,将用户自定义的Runnable对象添加进workQueue队列   
        if (isRunning(c) && workQueue.offer(command)) {
            //再次检查,获取线程池的控制状态
            int recheck = ctl.get();
            //线程池不处于RUNNING状态,将自定义任务从workQUEUE队列中移除
            if (! isRunning(recheck) && remove(command))
                //拒绝执行命令
                reject(command);
            else if (workerCountOf(recheck) == 0)   //当worker数量等于0
                addWorker(null, false);   //添加worker
        }
        else if (!addWorker(command, false))   //如果添加worker失败
            reject(command);   //拒绝执行命令
    }


(2)添加worker:      

1. 原子性的增加workerCount;     

2. 将用户给定的任务封装成为一个worker,并将此worker添加进workers集合中;    

3. 启动worker对应的线程,并启动该线程,运行worker的run方法;    

4. 回滚worker的创建动作,即将worker从workers集合中删除,并原子性的减少workerCount;     

源码:

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 &&         //状态大于等于SHUTDOWN,初始的ctl为RUNNING,小于SHUTDOWN
                ! (rs == SHUTDOWN &&      //状态为:SHUTDOWN
                   firstTask == null &&    //第一个任务为null
                   ! workQueue.isEmpty()))  //worker队列不为空
                return false;   //返回

            for (;;) {
                //worker数量
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||                              //worker数量大于等于最大容量
                    wc >= (core ? corePoolSize : maximumPoolSize))  //worker数量大于等于核心线程池大小或者最大线程池大小
                    return false;
                if (compareAndIncrementWorkerCount(c))     //比较并增加worker的数量
                    //跳出外层循环
                    break retry;
                //获取线程池的状态
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)   //此次的状态与上次获取的状态不相同
                    //跳过剩余部分,继续循环
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        //worker开始标识
        boolean workerStarted = false;
        //worker被添加标识 
        boolean workerAdded = false;
        Worker w = null;
        try {
            //初始化worker
            w = new Worker(firstTask);
            //获取worker对应的线程
            final Thread t = w.thread;
            if (t != null) {        //线程不为null
                //线程池锁
                final ReentrantLock mainLock = this.mainLock;
                //获取锁
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    //线程池的运行状态
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||      //小于SHUTDOWN
                        (rs == SHUTDOWN && firstTask == null)) {   //等于SHUTDOWN并且firstTash为null
                        if (t.isAlive()) // precheck that t is startable //线程刚添加进来,还未启动就存活
                            throw new IllegalThreadStateException();  //抛出线程状态异常
                        workers.add(w);  //将worker添加到workers集合中
                        int s = workers.size();  //获取workers的大小
                        if (s > largestPoolSize)  //队列大小大于largestPoolSize
                            largestPoolSize = s;   //重新设置largestPoolSize
                        workerAdded = true;   //设置worker已被添加标识
                    }
                } finally {
                    mainLock.unlock();  //释放锁
                }
                if (workerAdded) {    //worker被添加
                    t.start();  //开始执行worker的run方法
                    workerStarted = true;  //设置worker已开始标识
                }
            }
        } finally {
            if (! workerStarted)   //worker没有开始
                addWorkerFailed(w);  //添加worker失败,调用addWorkerFailed方法
        }
        return workerStarted;
    }


(3)执行任务:      

runWorker函数中会实际执行给定任务(即调用用户量写的run方法),并且当给定任务完成后,会继续从阻塞队列中取任务,直到阻塞队列为空(即任务全部完成)。     

在执行给定任务时,会调用钩子函数,则利用钩子函数可以完成用户自定义的一些逻辑,在runWorker中会调用到getTask函数和processWorkerExit钩子函数.     

源码如下:

final void runWorker(Worker w) {
    //获取当前线程
    Thread wt = Thread.currentThread();
    //获取w的firstTask
    Runnable task = w.firstTask;
    //设置w的firstTask为null
    w.firstTask = null;
    //释放锁(设置state为0,允许中断)
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {   //任务不为null或者阻塞队列还存在任务
            //获取锁
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||    //线程池的运行状态至少应该高于STOP
                 (Thread.interrupted() &&     //线程被中断
                  runStateAtLeast(ctl.get(), STOP))) &&  //再次检查,线程池的运行状态至少应该高于STOP
                !wt.isInterrupted())   //wt线程(当前线程)没有被中断
                wt.interrupt();   //中断wt线程(当前线程)
            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;
                //增加给worker完成的任务数量
                w.completedTasks++;
                w.unlock();  //释放锁
            }
        }
        completedAbruptly = false;
    } finally {
        //处理完成后,调用钩子函数
        processWorkerExit(w, completedAbruptly);
    }
}


(4)获取任务:      

getTask函数用于从workerQueue阻塞队列中获取Runnable对象,用于是阻塞队列,所以支持有限时间等待(poll)和无限时间等待(task)。        

在该函数中还会响应shutDown和shutDownNow函数的操作,若检查到线程池处于SHUTDOWN或STOP状态,则会返回null,而不返回阻塞队列中的Runnable对象。       

源码:        

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {   //无限循环,确保操作成功
        int c = ctl.get();   //获取线程池的控制状态
        int rs = runStateOf(c);  //运行的状态

        // Check if queue empty only if necessary.
        //rs大于等于SHUTDOWN(表示调用了shutDown)并且(大于等于STOP (调用了shutDownNow)或者worker阻塞队列为空)
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {  
            decrementWorkerCount();  //减少worker的数量
            return null;  //返回null,不执行任务
        }

        int wc = workerCountOf(c);  //获取worker数量

        // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;  //是否允许coreThread超时或者workerCount大于核心大小

        if ((wc > maximumPoolSize || (timed && timedOut))  //worker数量大于maximumPoolSize
            && (wc > 1 || workQueue.isEmpty())) {  //workerCount大于1或者worker阻塞队列为空(在阻塞队列不为空时,需要保证至少有一个wc
            if (compareAndDecrementWorkerCount(c))  //比较并减少workerCount
                return null;  //返回null, 不执行任务,该worker会退出
            continue;   //跳过剩余部分,继续循环
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :   //等待指定时间
                workQueue.take();   //一直等待,直到有元素
            if (r != null)
                return r; 
            timedOut = true;   //等待指定时间后,没有获取元素,则超时
        } catch (InterruptedException retry) {
            timedOut = false;   //抛出被中断异常,重试,没有超时
        }
    }
}


(5)钩子函数:      

processWorkerExit函数是在worker退出时调用到的钩子函数,则引起worker退出时主要因素有:       

1. 阻塞队列已经为空,即没有任务可以运行了;     

2. 调用了shutDown和shutDownNow函数;     

次函数会根据是否中断了空闲线程来确定是否减少workerCount的值,并且将worker从workers集合中移除并且会尝试终止线程池。      

源码如下:     

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    //如果被中断,则需要减少workerCount
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();
    
    //获取可重入锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();  //获取锁
    try {
        //将worker完成的任务添加到总的完成任务中
        completedTaskCount += w.completedTasks;   
        workers.remove(w);  //从workers集合中移除worker
    } finally {
        mainLock.unlock();  //释放锁
    }

    tryTerminate();  //尝试终止

    //获取线程池的控制状态
    int c = ctl.get(); 
    if (runStateLessThan(c, STOP)) {   //小于STOP的运行状态
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())   //允许核心超时并且workQueue阻塞队列不为空
                min = 1;
            if (workerCountOf(c) >= min)   //workerCount大于等于min
                //直接返回
                return; // replacement not needed  
        }
        addWorker(null, false);   //调用添加worker方法
    }
}


(6)关闭线程池:     

shutdown(): 只有当线程任务都结束时,才会关闭线程;当调用了此方法时,它不会立即执行,而是要等线程池中的任务都结束时才会执行;         

shutdownNow(): 当调用了此方法时,它会把线程池中正在执行的任务杀死,而立即关闭线程池;     

源码:   

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock; //获取线程池的锁
    mainLock.lock();  //加锁
    try {
        //检查shutdown权限
        checkShutdownAccess();
        //设置线程池控制状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        //中断空闲worker
        interruptIdleWorkers();
        //调用shutdown钩子函数
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();   //释放锁
    }
    tryTerminate();   //尝试终止
}


public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock; //获取线程池的锁
    mainLock.lock();  //加锁
    try {
        //检查shutdown权限
        checkShutdownAccess();
        //设置线程池控制状态为STOP
        advanceRunState(STOP);
        //中断线程集合
        interruptWorkers();
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}


(7)尝试终止:    

final void tryTerminate() {
    for (;;) {   //无限循环,确保操作成功
        //获取线程池控制状态
        int c = ctl.get();
        if (isRunning(c) ||                                        //线程池的运行状态为RUNNING
            runStateAtLeast(c, TIDYING) ||                         //线程池的运行状态最小要大于TIDYING
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))  //线程池的运行状态为SHUTDOWN并且workQueue队列不为null
            return;  //不能终止,直接返回
        if (workerCountOf(c) != 0) { // Eligible to terminate  //线程池正在运行的worker数量不为0,
            interruptIdleWorkers(ONLY_ONE);  //仅仅中断一个空闲的worker      
            return;
        }
        //获取线程池的锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();  //加锁
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {  //比较并设置线程池控制状态为TIDYING
                try {
                    terminated();   //终止钩子函数
                } finally {
                    //设置线程池控制状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    //释放在termination条件上等待的所有线程
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();  //释放锁
        }
        // else retry on failed CAS
    }
}


(8)中断空闲workers:    

private void interruptIdleWorkers(boolean onlyOne) {
    //获取线程池的锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();  //加锁
    try {
        for (Worker w : workers) {  //遍历workers队列
            Thread t = w.thread;
            if (!t.isInterrupted() && w.tryLock()) {  //线程未被中断并且成功获取到锁
                try {
                    t.interrupt();  //中断线程
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();  //释放锁
                }
            }
            if (onlyOne)  //若只中断一个,则跳出循环
                break;
        }
    } finally {
        mainLock.unlock();  //释放锁
    }
}

实例代码:

public class ThreadPoolDemo1 {

    public static void main(String[] args) {

        //ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 10,TimeUnit.DAYS, new ArrayBlockingQueue<>(10), new ThreadPoolExecutor.DiscardOldestPolicy());
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 10,
                TimeUnit.DAYS, new ArrayBlockingQueue<>(10), new ThreadPoolExecutor.CallerRunsPolicy());
        AtomicInteger count = new AtomicInteger();
        for(int i = 0;i < 100;i++){
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    count.getAndIncrement();
                }
            });
        }
        //threadPoolExecutor.shutdown();  //等待线程任务执行完毕后,才会关闭线程池
        threadPoolExecutor.shutdownNow();  //立即关闭线程池,杀死没有运行完毕的线程任务

        while(Thread.activeCount() > 1){

        }
        System.out.println(count.get());
    }
}


public class ExecutorDemo {

    public static void main(String[] args) {
        ExecutorService pool =  Executors.newFixedThreadPool(10);  //可以指定线程池的大小
        //ExecutorService pool =  Executors.newCachedThreadPool()  //带缓存的线程池
        //ExecutorService pool =  Executors.newSingleThreadExecutor();  //单个线程的线程池
        //ExecutorService pool =  Executors.newScheduledThreadPool(10);  //创建带有计划任务的线程池

        ThreadFactory tf = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                return t;
            }
        };

        while (true){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }


    }
}

本文基于jdk1.8版本。   关于更多并发相关的知识,后续会不断更新,感谢诸君的支持!

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.5万
获赞与收藏
8507

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消