线程池源码浅析

并发的这一些东西最近都在看,看着看着发现其实还有挺有意思的,不管能理解多少,其实也是有收获的,万事开头难。当真正有兴趣了,就能看就去了,如果实在看不懂,从相关博客开始看。记住,找个凉快时间,有个舒适的心情看这些枯燥的东西才好接受!!!下一篇我说说AQS,感觉这玩意挺好,各种锁都使用了他,还有一些同步工具等也使用了AQS!

概述
  • 线程池的主要作用
    • 如果使用了多线程,那么多线程会比单线程来说会提高效率(如果使用多线程并没有提升效率那我们就不要用线程池了)
    • 提高资源的利用率,让程序可以进行并发并行执行
    • 有效的管理资源,内存资源和CPU资源是有限的,线程池创建之后需要进行管理,每次创建线程都需要系统分配资源。(如果是CPU密集型,那么线程数=核心数,因为对CPU需求量高,所以线程数多与CPU核心数也没有多与的CPU资源来处理线程。如果是IO型的,那么可以多创建一些线程,IO型主要等待IO操作时间比较长,CPU利用率相对较低,所以可以多创建一些线程)
  • 一些技术的使用:
    • BlockingQueue阻塞队列的使用在任务获取线程时,
    • AQS:独占锁
逻辑梳理
  • 线程池的几个重要参数:
    • corePoolSize:核心线程数。阻塞队列没有任务时,线程池可以创建的线程
    • maxPoolSize:最大线程数。当阻塞队列满了以后,线程池可以创建的线程数为maxPoolSize
    • keepAliveTime:线程的存活时间,通常是指超出corePoolSize的线程数的存活时间,也可以通过方法调用设置corePoolSize中线程的存活时间。
    • unit:存活时间的单位:
    • BlockingQueue:阻塞队列(有界队列,避免OOM,如果是无界队列,会导致队列不断的变大,内存空间是有限的,同时该部分内存无法在OOM时回收,强引用),用来存储等待执行的任务。当前线程数大与等于核心线程数时,将任务存入阻塞队列。
    • ThreadFactory:创建线程的工厂。线程池中的线程由该工厂创建
    • RejectedExecutionHandler:表示拒绝任务时的策略,当阻塞队列满时,对于来请求线程池线程的处理方式
  • 线程池的处理流程:线程池的设计原理中,分为两部分,一部分是创建worker(创建线程A,指定当前线程要执行的任务B),一部分执行任务,由创建线程A.start启动创建的线程,真正处理执行的传递过来的执行的任务B,同时任务B本身执行完成后,还会从阻塞队列中获取任务执行。execute方法调用(submit也会调用到该方法)
    • 当前线程数<核心线程数,创建worker
    • 当前线程数>核心线程数&&阻塞队列没有满,将任务添加到阻塞队列
    • 当前线程数>核心线程数&&阻塞队列已满&&当线程数<最大线程数,创建worker
    • 当前线程数>核心线程数&&阻塞队列已满&&当线程数>最大线程数,执行RejectedExecutionHandler拒绝策略
一开始的困惑
  • 几个状态:状态是指整个线程池的状态,而不是单个线程的状态,实际上线程池中线程的数量使用定义的多个size控制,而线程由程序内部进行管理
  • 执行任务时,任务执行完自身的任务后,会从线程池中获取任务进行执行,该任务的执行调用的是run方法,而不是创建新的线程(该部分的设计是启动任务和执行任务分开的过程)–找个地方有意思,后面我会在代码中具体说说,这里也是线程创建的任务重复执行请求的地方
  • AtomicInteger ctl是一个32位操作数,高三位表示线程池的状态,低29位表示线程池数,通过runStateOf方法获得线程池的状态,workCountOf方法是获取当前线程数,个人感觉这么设计的好处是保持一定的灵活性,同时扩容线程池大小或者增加线程池状态时,更改COUNT_BITS即可。
  • workCountOf方法是个递增的过程,每当增长一个,才会向worker中添加一个线程,知道线程数大与核心线程数(此种情况计假设当前阻塞队列没有满的情况下),当达到线程容量时,就不在创建新创建线程了,新来的任务直接添加到阻塞队列,由已经已经创建好的任务进行处理,即调用run方法。
源码浅读
  • 主流程:该部分是选择直接创建worker还是添加阻塞队列还是抛弃任务等等
public void execute(Runnable command) {
       if (command == null)
           throw new NullPointerException();
       int c = ctl.get();
       //当前线程数<核心线程数 向工作集中添加线程
       if (workerCountOf(c) < corePoolSize) {
           if (addWorker(command, true))//具体的添加线程逻辑,true\false指当先
               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);
       }
       //当前线程数>核心线程数&&阻塞队列已满&&当线程数<最大线程数,创建worker
       else if (!addWorker(command, false))
            //当前线程数>核心线程数&&阻塞队列已满&&当线程数>最大线程数,执行RejectedExecutionHandler拒绝策略
           reject(command);
   }

  • addWorker方法:具体的创建、添加线程逻辑,true\false指当线程数和最大线程数比较还是核心线程数比较,这一部分分为两部分,第一部分是线程池状态和线程池数量的判断,看是否创建worker,第二部分创建worker,启动线程。如果当前线程数大与核心线程数或者最大线程数,将不再执行第二部分,在第一部分判断的过程会直接退出,将任务添加到阻塞队列
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;

          for (;;) {
              int wc = workerCountOf(c);
              if (wc >= CAPACITY ||
                  wc >= (core ? corePoolSize : maximumPoolSize)) //根据core是false还是true取当前的最大线程数
                  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
          }
      }
      //第二部分创建worker,启动线程,
      boolean workerStarted = false;
      boolean workerAdded = false;
      Worker w = null;
      try {
          w = new Worker(firstTask);//一会具体看看这个,创建worker,任务都是在这里执行的
          final Thread t = w.thread; //worker中创建的线程
          if (t != null) {
              final ReentrantLock mainLock = this.mainLock;
              mainLock.lock();
              try {
                  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();//启动woker中的线程
                  workerStarted = true;
              }
          }
      } finally {
          if (! workerStarted)
              addWorkerFailed(w);
      }
      return workerStarted;
  }
  • Worker类:该部分为执行任务的部分,不管是最初创建线程,携带任务过来的时候,还是后来阻塞队列中有任务的时候,真正执行的任务的过程都在这里。
  private final class Worker extends AbstractQueuedSynchronizer
          implements Runnable{
          /** Thread this worker is running in.  Null if factory fails. */
          final Thread thread;
          /** Initial task to run.  Possibly null. */
          Runnable firstTask;
          /** Per-thread task counter */
          volatile long completedTasks;
          //构造方法,创建worker,firstTask是当前任务,thread是线程,该thread的目的主要是启动线程,执行该类的run方法
          Worker(Runnable firstTask) {
              setState(-1); // inhibit interrupts until runWorker,该阶段线程没有启动任务,所以不需要中断
              this.firstTask = firstTask;
              this.thread = getThreadFactory().newThread(this);
          }
          public void run() {
              runWorker(this);
          }
        }
  • runWorker:这部分为执行线程的部分,当线程池的容量多达到核心线程数,或者最大线程数以后,就不在创建线程了,而是由任务不断的从阻塞队列中获取任务执行,在这里有个阻塞队列的应用,
  final void runWorker(Worker w) {
                Thread wt = Thread.currentThread();
                Runnable task = w.firstTask;
                w.firstTask = null;
                w.unlock(); // allow interrupts 更改AQS 中state的状态,使任务处于可被中断的状态
                boolean completedAbruptly = true;
                try {
                    while (task != null || (task = getTask()) != null) {
                        w.lock();//每个woker中的任务只能由一个任务来执行
                        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 {
                    //释放worker中的线程
                    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. 判断线程池的状态和队列是否为空,减少线程数
                        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                            decrementWorkerCount();
                            return null;
                        }
                        int wc = workerCountOf(c);
                        // Are workers subject to culling? 如果当前线程大与核心线程数,而且有线程处于空闲的状态,那么释放线程数
                        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

                        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();
                            if (r != null)
                                return r;
                            timedOut = true;
                        } catch (InterruptedException retry) {
                            timedOut = false;
                        }
                    }
                }

应用
  • 目前线程池的使用,还处于比较浅显的阶段,工作中也就是用线程处理加快一些数据结果的处理,当前有200万个数据要加工,利用线程池和CountDownLatch进行处理就好了,如果需要返回结果就用FutureTask,如果不需要结果用Runnable就可以,CountDownLatch的作用是保证线程都计算完成后,才能玩下进行
  • 注意点:创建线程要自己指定核心线程数等。
    • FixThreadPool和SingleTheadExecutor:队列长度为Integer.MAX_VALUE,可能堆积大量的请求,导致OOM
    • CachedThreadPool和ScheduledThreadPool:允许创建的线程数是Integer.maxValue,可能会创建大量的线程导致OOM
参考

https://www.jianshu.com/p/1f5195dcc75b
https://www.cnblogs.com/dolphin0520/p/3932921.html
https://www.jianshu.com/p/a2616dcb7f13

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值