Java并发编程之Java线程池

Java线程池:

  1. 线程池的核心配置参数:

    //线程等待任务的超时时间,当线程池的线程个数超过corePoolSize时生效,当线程等待任务的时间超过keepAliveTime时,线程池会停止超过corePoolSize部分的线程。
    private volatile long keepAliveTime;
    
    //默认false,表示核心线程等待任务不会超时;如果为ture,则允许核心线程超时,也就是说在无任务时,核心线程也会被线程池停止。
    private volatile boolean allowCoreThreadTimeOut;
    
    //线程池保持的线程最小数量,当allowCoreThreadTimeOut=true时,线程池保持的线程数量为0
    private volatile int corePoolSize;
    
    //线程池的允许的线程的最大个数,当线程数到达maximumPoolSize时,如果还有新的任务到来线程池会执行拒绝策略
    private volatile int maximumPoolSize;
    
    //阻塞队列,当线程池的线程数到达corePoolSize时,新接收的任务会放进阻塞队列,阻塞队列又分为有界队列和无界队列;如果是无界队列则任务一直往队列里放,如果是有界队列当任务数超过队列界限时,当corePoolSize < maximumPoolSize时,会启动新的线程。
    private final BlockingQueue<Runnable> workQueue;
    
    //线程池中保存线程的容器,记录了当前线程池中的线程信息
    private final HashSet<Worker> workers = new HashSet<Worker>();
    
    //线程池控制变量,低29位表示线程池的数量,高3位表示线程池状态
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
  2. 线程池的工作原理: 线程池接收到一个新任务时,会判断当前已经启动的线程个数是否超过corePoolSize,若未超过则启动一个新的线程来处理请求;否则,判断阻塞队列workQueue中的任务个数是否超过上界,若未超过上界,则将任务放入阻塞队列,否则,判断线程池中的当前线程个数是否超过maximumPoolSize,若超过则执行拒绝策略,否则启动新的线程执行任务。

  3. 线程池的任务提交方法:

    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);
    }
    
  4. addWorker方法: 该方法主要用于向线程池中添加新的线程

    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;
            //CAS检测线程池的线程数量
            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;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
    
        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 {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    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

  5. 线程池的Worker:

    //Worker继承自AQS,从阻塞队列中取任务,为啥处理时要进行加锁处理?请看下文解析
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        //运行该worker的线程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        //线程执行的任务个数
        volatile long completedTasks;
        /**
             * 构造函数,构造一个worker
             */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
    
        //线程中执行的方法,该方法是一个循环,在内部会不断获取阻塞队列中的线程并执行
        public void run() {
            runWorker(this);
        }
        
        final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            w.unlock(); // allow interrupts
            boolean completedAbruptly = true;
            try {
                //循环取阻塞队列中的任务,线程池关闭的时候或者线程池中线程个数超过maximumPoolSize或者获取任务超时的时候getTask()会返回null,退出循环
                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);
            }
        }
    }
    
  6. ThreadPoolExecutor提供了两个方法来关闭线程池:

    1. shutdown(),关闭线程池,在阻塞队列中的任务会继续执行,但此时线程池不接收新的任务。
    2. shutdownNow(),立即关闭线程池,不接收新的任务,并尝试停止所有正在执行的任务,该方法是通过Thread.interrupt()来中断执行线程,因此只有响应中断的任务才能通过该方法停止,否则只能等待任务执行完毕。
    3. 需要说明的是,以上两种停止线程池的方法都不会等待线程池停止完毕,如果要等待线程池停止完毕需要在调用关闭方法之后,再调用awaitTermination方法,该方法会阻塞当前线程一定的时间,直到线程池停止或者当前线程超时。
posted @ 2022-02-21 22:16  隐风  阅读(88)  评论(0)    收藏  举报