ThreadPoolExecutor源码分析及阻塞提交任务方法

ThreadPoolExecutor源码

ThreadPoolExecutor 基本使用参考:ThreadPoolExecutor执行过程分析

线程池状态标志

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

ctl 保存了线程池的运行状态(runState)和线程池内有效线程数量(workerCount)。

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

用 ctl 的高3位来表示线程池的运行状态, 用低29位来表示线程池内有效线程的数量。ctlOf() 方法用于计算出ctl的值。runStateOf()和workerCountOf()方法分别通过CAPACITY来计算得到其runState和workerCount,CAPACITY=29个1。

线程池的运行状态:

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
//shutdown() -> SHUTDONW , 不加新任务,继续执行阻塞队列中的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
//shutdownNow() -> STOP, 中断一切操作。
private static final int STOP       =  1 << COUNT_BITS;
//线程池没有线程,阻塞队列没有任务 -> TIDYING
private static final int TIDYING    =  2 << COUNT_BITS;
//terminated() -> TERMINATED
private static final int TERMINATED =  3 << COUNT_BITS;

execute(Runnable command)

/**
 * Executes the given task sometime in the future.  The task
 * may execute in a new thread or in an existing pooled thread.
 *
 * If the task cannot be submitted for execution, either because this
 * executor has been shutdown or because its capacity has been reached,
 * the task is handled by the current {@code RejectedExecutionHandler}.
 *
 * @param command the task to execute
 * @throws RejectedExecutionException at discretion of
 *         {@code RejectedExecutionHandler}, if the task
 *         cannot be accepted for execution
 * @throws NullPointerException if {@code command} is null
 */
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();
    if (workerCountOf(c) < corePoolSize) {
        //如果线程池中线程数没有达到corePoolSize,则新增线程(worker)
        if (addWorker(command, true))
            return;
        //更新c值。
        c = ctl.get();
    }
    //线程池处于RUNNING状态,并且阻塞队列未满
    //workQueue.offer(command)是非阻塞方法,当队列满时直接返回false(例如,SynchronousQueue如果没有线程在阻塞take,则返回false)
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //再次检查状态,如果发现不是RUNNING状态,则remove掉刚才offer的任务。
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //如果有效线程数==0,添加一个线程,而不去启动它。??
        //怎么会==0?
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //如果不是RUNNING状态,或者阻塞队列已满,则添加线程
    //如果不能添加,则reject。
    //false 表示添加的线程属于maximumPoolSize,如果线程数已经达到maximumPoolSize,则reject
    else if (!addWorker(command, false))
        reject(command);
}

20160228204222307

BlockingQueue 的一些操作方法

抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e, time, unit)
移除remove()poll()take()poll(time, unit)
检查element()peek()不可用不可用

addWorker(Runnable firstTask, boolean core)

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 &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            //1. 处于 STOP, TYDING 或 TERMINATD 状态 并且 
            //2. 不是SUHTDOWN 或者 firsttask != null 或 queue不为空
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            //wc大于最大容量。
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                //没有空余的线程了。
                return false;
            //有效线程数加一,加一成功后break
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            //runState改变,从头执行逻辑。
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
            //else runState 没变,重新去执行加一操作。
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //创建worker
        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) {
                //添加成功,启动线程
                //启动后执行runWorker(this);
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

runWorker(Worker w)

运行worker,该线程不断的getTask()从队列中获取任务,然后 task.run();运行。只要队列中有值则不断循环。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        //getTask()方法是个无限循环, 会从阻塞队列 workQueue中不断取出任务来执行.
        //addWorker(null, false);情况,task==null,这样就需要getTask从队列中取任务执行(自己不带任务)。直到getTask返回null
        while (task != null || (task = getTask()) != 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) ||
                 (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()

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.
        // STOP以上状态,或者SHUTDOWN状态下queue为空,即都没有任务要执行了。
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            //线程数减一
            decrementWorkerCount();
            //该线程退出。
            return null;
        }
        //下面都是RUNNING状态,或SHUTDOWN状态queue!=null

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //设置了allowCoreThreadTimeOut,或者线程数大于core线程数。
        //是否剔除超时的线程?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		
        // 通过返回 null 结束线程。
        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();
           	//线程已经准备好,正在take(),没有什么标志位?
            
            //取出runnable 返回
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

ThreadPoolExecutor阻塞添加任务

使用semaphore限流ThreadPoolExecutor(失效及原因)

考虑到当线程池满时(任务数 > maximumPoolSize + Queue.size()),会执行饱和策略。默认AbortPolicy ,抛出RejectedExecutionException。

怎么能避免线程池拒绝提交的任务呢?首先想到通过信号量Semaphore来控制任务的添加。代码如下:

注意:该代码是无效的。

Semaphore semaphore;

/**
 * 使用semaphore,控制提交任务速度
 * @throws InterruptedException
 * @throws ExecutionException
 */
@Test
public void test555() throws InterruptedException, ExecutionException {
   ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 7, 10, TimeUnit.SECONDS, new SynchronousQueue<>());
   //信号量设置为线程池最大线程数
   semaphore = new Semaphore(threadPoolExecutor.getMaximumPoolSize());
   ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService(threadPoolExecutor);

   Runnable runnable = new Runnable() {
      @Override
      public void run() {
         for (int i = 0; i < 50; i++) {
            String name = "name_" + i;
            TestCallable testCallable = new TestCallable(name);
            try {
               //RetryUtil.createThreadPoolExecutor()
               semaphore.acquire();

               executorCompletionService.submit(testCallable);
               logger.info("+++添加任务 name: " + name + poolInfo(threadPoolExecutor));
               //threadPoolExecutor.submit(testCallable);
            } catch (RejectedExecutionException e) {
               logger.info("拒绝:" + name);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            try {
               //添加任务间隔200ms
               Thread.sleep(200);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
         finishState = 1;
      }
   };

   Thread addThread = new Thread(runnable);
   addThread.start();

   //logger.info(" taskCount: " + threadPoolExecutor.getTaskCount());

   //添加的任务有被抛弃的。taskCount不一定等于添加的任务。
   int completeCount = 0;
   while (!(completeCount == threadPoolExecutor.getTaskCount() && finishState == 1)) {
      Future<String> take = executorCompletionService.take();
      String taskName = null;

      try {
         taskName = take.get();
         //有可能线程池还没准备好?
         semaphore.release();
         System.out.println("???" + take.isDone());

      } catch (InterruptedException e) {
         e.printStackTrace();
      } catch (ExecutionException e) {
         logger.info(e.getMessage());
      }

      logger.info("---完成任务 name: "
            + taskName + poolInfo(threadPoolExecutor)
            + " finishTask:" + (++completeCount));

   }

   addThread.join();


   while (threadPoolExecutor.getPoolSize() > 0) {
      Thread.sleep(1000);
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
      logger.info(simpleDateFormat.format(new Date()) + poolInfo(threadPoolExecutor));
   }

   // Tell threads to finish off.
   threadPoolExecutor.shutdown();
   // Wait for everything to finish.
   while (!threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
      logger.info("complete");
   }

}

public String poolInfo(ThreadPoolExecutor threadPoolExecutor) {
   return " ActiveCount: " + threadPoolExecutor.getActiveCount()
         + " poolSize: " + threadPoolExecutor.getPoolSize()
         + " queueSize: " + threadPoolExecutor.getQueue().size()
         + " taskCount: " + threadPoolExecutor.getTaskCount();
}

只是在submit之前添加semaphore.acquire(); 在获取future后,添加semaphore.release();。

但这样依然会产生RejectedExecutionException。

通过源码分析原因,

当线程池中线程已满,并且都处于忙碌状态。此时semaphore的值==线程池线程数,addThread被semaphore.acquire()阻塞,禁止submit新任务。当线程池中一个线程t1执行了runWorker(Worker w)中的task.run(),main线程就可以执行Future<String> take = executorCompletionService.take()获取结果并semaphore.release()释放信号量。

释放信号量semaphore后,addThread线程可以submit新任务,假设此时t1线程还没有执行到getTask() 中的poll()和take()方法。此时workQueue队列依然是满的。

Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();

而addThread已经执行到execute()的

    if (isRunning(c) && workQueue.offer(command)) {

当workQueue已满,offer() 直接返回false(正确的顺序应该是等t1线程执行到workQueue.take()后addThread再开始执行workQueue.offer(command)。)。执行execute() 如下逻辑

else if (!addWorker(command, false))
    reject(command);

addWork()中,wc = maximumPoolSize 返回false。

 if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                //没有空余的线程了。
                return false;

执行reject(),抛出RejectedExecutionException。

使用自定义队列(不建议)

public class LimitedQueue<E> extends LinkedBlockingQueue<E> 
{
    public LimitedQueue(int maxSize)
    {
        super(maxSize);
    }

    @Override
    public boolean offer(E e)
    {
        // turn offer() and add() into a blocking calls (unless interrupted)
        try {
            put(e);
            return true;
        } catch(InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
        return false;
    }

}

其思想就是替换BlockingQueue中的offer()方法为put()方法,这样execute() 中的workQueue.offer(command),就变成put(),阻塞添加任务,不会存在workQueue.offer() 返回false的情况。

//void execute(Runnable command) 中代码

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);

但这样的问题是下面的else if (!addWorker(command, false)) 代码逻辑将无法执行,导致的结果就是,只针对corePoolSize==maxPoolSize 时有效。不建议这么做。

自定义RejectedExecutionHandler

RejectedExecutionHandler block = new RejectedExecutionHandler() {
  rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
     executor.getQueue().put( r );
  }
};

ThreadPoolExecutor pool = new ...
pool.setRejectedExecutionHandler(block);

通过自定义RejectedExecutionHandler,在reject时调用Queue的put()方法,阻塞式添加任务。

使用CallerRunsPolicy

其实忙活一圈,发现最简单的方式就是使用ThreadPoolExecutor.CallerRunsPolicy。

CallerRunsPolicy被拒绝的任务,谁submit的谁执行。想想之前的各种阻塞也对,负责添加任务的线程因为线程池满了就阻塞在那里,还不如帮着执行一些任务..

Reference

转载于:https://my.oschina.net/u/1245414/blog/1836751

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值