13_线程池的实际操作

  • 参考 https://www.cnblogs.com/kuoAT/p/6714762.html

  1. 线程池一般就是这么用的

     public static void main(String[] args) {
    
         ExecutorService executorService = Executors.newFixedThreadPool(8);
     
         Future<Integer> future = executorService.submit(new Callable<Integer>() {
             @Override
             public Integer call() throws Exception {    
                 // doSomething();
                 return null;
             }
         });
     
         try {
             System.out.println(future.get());
         } catch (InterruptedException | ExecutionException e) {
             e.printStackTrace();
         }
     }
    

    也就是说 submit 一个任务,然后用 future 对象来异步获取结果

  2. 几个类和接口的继承关系是

    Executor <— ExecutorService <— AbstractExecutorService <— ThreadPoolExecutor

    而创建线程池的时候使用的其实就是 ThreadPoolExecutor

         public static ExecutorService newFixedThreadPool(int nThreads) {
             
             return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
         }
    
  3. 进入 submit 方法是这样的

     public <T> Future<T> submit(Callable<T> task) {
     
         if (task == null) 
             throw new NullPointerException();
     
         RunnableFuture<T> ftask = newTaskFor(task);
    
         execute(ftask);
     
         return ftask;
     }
    

    可以看到 submit 方法其实是给 execute 方法套了一个壳子,然后返回一个 future 对象,所以重点还是要看 execute 方法

  4. 在了解将任务提交给线程池到任务执行完毕整个过程之前,我们先来看一下 ThreadPoolExecutor 类中其他的一些比较重要成员变量

     public class ThreadPoolExecutor extends AbstractExecutorService {
    
         ...
    
         private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));   // 用来代表有效线程数量和线程池状态
    
         private final BlockingQueue<Runnable> workQueue;  // 任务缓存队列,用来存放等待执行的任务
    
         private final ReentrantLock mainLock = new ReentrantLock();  // 线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
    
         private final HashSet<Worker> workers = new HashSet<Worker>();  // 用来存放工作集
    
         private final Condition termination = mainLock.newCondition();  // 终止线程池时的条件变量
    
         private volatile long keepAliveTime;  // 线程存活时间
    
         private volatile boolean allowCoreThreadTimeOut;   // 是否允许为核心线程设置存活时间
    
         private volatile int corePoolSize;  //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
    
         private volatile int maximumPoolSize;   // 线程池最大能容忍的线程数
    
         private volatile int poolSize;       // 线程池中当前的线程数
    
         private volatile RejectedExecutionHandler handler;   // 任务拒绝策略
    
         private volatile ThreadFactory threadFactory;   // 线程工厂,用来创建线程
    
         private int largestPoolSize;   // 用来记录线程池中曾经出现过的最大线程数
    
         private long completedTaskCount;   // 用来记录已经执行完毕的任务个数
    
         ...
     }
    
  5. 接下来看 execute 方法

     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) {
             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 会开新线程

  • addWorker 方法

      private boolean addWorker(Runnable firstTask, boolean core) {
    
          retry:
          for (int c = ctl.get();;) {
              // Check if queue empty only if necessary.
              if (runStateAtLeast(c, SHUTDOWN)
                  && (runStateAtLeast(c, STOP)
                      || firstTask != null
                      || workQueue.isEmpty()))
                  return false;
    
              for (;;) {
                  if (workerCountOf(c)
                      >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                      return false;
              
                  if (compareAndIncrementWorkerCount(c))
                      break retry;
                  c = ctl.get();  // Re-read ctl
              
                  if (runStateAtLeast(c, SHUTDOWN))
                      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 c = ctl.get();
    
                      if (isRunning(c) ||
                          (runStateLessThan(c, STOP) && 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;
      }
    

    这个方法前半部分是各种 CAS 操作,后半部分是在加锁的前提下,尝试添加worker,让worker的新线程start

  • reject 方法用到了策略模式,将四种基本的拒绝策略封装好交给各自实现

      final void reject(Runnable command) {
          handler.rejectedExecution(command, this);
      }
    

猜你喜欢

转载自blog.csdn.net/captxb/article/details/100861527