了解下JUC的线程池学习一

 1.设计者(Doug Lea)

 

2.ThreadPoolExecutor实现的最顶层接口

    public interface Executor {
           void execute(Runnable command);
    }

    说明:ExecutorService提供了很多扩展方法底层基本上是基于Executor#execute()方法进行扩展   

               hreadPoolExecutor的源码从JDK8JDK11基本没有变化,这里是基于JDK11的实现

 

3.ThreadPoolExecutor的实现原理

    第一点:ThreadPoolExecutor里面使用到JUC同步器框架AbstractQueuedSynchronizer

                 (俗称AQS)、大量的位操作、CAS操作。

    第二点:ThreadPoolExecutor提供了固定活跃线程(核心线程)、

                  额外的线程(线程池容量 - 核心线程数这部分额外创建的线程,下面称为非核心线程)、

                  任务队列以及拒绝策略这几个重要的功能。

 

 

4.线程池使用同步器的方面

   第一个:全局锁mainLock成员属性,是可重入锁ReentrantLock类型,

                 主要是用于访问工作线程Worker集合和进行数据统计记录时候的加锁操作。

   第二个:条件变量terminationCondition类型,

                 主要用于线程进行等待终结awaitTermination()方法时的带期限阻塞。

    第三个:任务队列workQueueBlockingQueue<Runnable>类型,

                  任务队列,用于存放待执行的任务。

    第四个:工作线程,内部类Worker类型,是线程池中真正的工作线程对象。

 

5.核心线程实现

   1)要实现核心线程需要注意点:

       第一点:暂时不考虑任务执行异常情况下的处理。

       第二点:任务队列为无界队列。

       第三点:线程池容量固定为核心线程数量。

       第四点:暂时不考虑拒绝策略。

    2)实现

         public class CoreThreadPool implements Executor {

                  private BlockingQueue<Runnable> workQueue;
                  private static final AtomicInteger COUNTER = new AtomicInteger();
                  private int coreSize;
                  private int threadCount = 0;
                  public CoreThreadPool(int coreSize) {
                          this.coreSize = coreSize;
                          this.workQueue = new LinkedBlockingQueue<>();
                  }
                  @Override
                  public void execute(Runnable command) {
                           if (++threadCount <= coreSize) {
                                 new Worker(command).start();
                          } else {
                                try {
                                    workQueue.put(command);
                                } catch (InterruptedException e) {
                                        throw new IllegalStateException(e);
                                }
                          }
                   }
                  private class Worker extends Thread {
                        private Runnable firstTask;
                        public Worker(Runnable runnable) {
                                super(String.format("Worker-%d", COUNTER.getAndIncrement()));
                                this.firstTask = runnable;
                         }
                       @Override
                       public void run() {
                            Runnable task = this.firstTask;
                            while (null != task || null != (task = getTask())) {
                                  try {
                                     task.run();
                                  } finally {
                                     task = null;
                                  }
                             }
                        }
                   }
                   private Runnable getTask() {
                          try {
                              return workQueue.take();
                          } catch (InterruptedException e) {
                             throw new IllegalStateException(e);
                          }
                     }
                 public static void main(String[] args) throws Exception {
                         CoreThreadPool pool = new CoreThreadPool(5);
                         IntStream.range(0, 10).forEach(i -> pool.execute(() ->
                                System.out.println(String.format("Thread:%s,value:%d", Thread.currentThread().getName(), i))));
                                Thread.sleep(Integer.MAX_VALUE);
                          }
                  }
              注:核心线程是懒创建的,如果线程空闲的时候则阻塞在任务队列take()方法,
                     其实对于ThreadPoolExecutor也是类似这样实现,只是如果使用了keepAliveTime并且允许核心线程超时
                   (allowCoreThreadTimeOut设置为true)则会
                     使用BlockingQueue#poll(keepAliveTime)进行轮询代替永久阻塞。
 
6.非核心线程
    参数了解下:maximumPoolSize(线程池最大线程数)和corePoolSize(核心线程数)
         当任务队列是有界的阻塞队列,核心线程满负载,任务队列已经满的情况下,
        会尝试创建额外的maximumPoolSize - corePoolSize个线程去执行新提交的任务。
    功能:
          第一点:一定条件下会创建非核心线程去执行任务,非核心线程的回收周期(线程生命周期终结时刻)是keepAliveTime
                线程生命周期终结的条件是:下一次通过任务队列获取任务的时候并且存活时间超过keepAliveTime
           第二点:提供拒绝策略,也就是在核心线程满负载、任务队列已满、非核心线程满负载的条件下会触发拒绝策略
     源码分析:
           总括下:先分析线程池的关键属性,接着分析其状态控制,最后重点分析ThreadPoolExecutor#execute()方法。
           关键属性:              
                public class ThreadPoolExecutor extends AbstractExecutorService {
                        // 控制变量-存放状态和线程数
                        private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
                        // 任务队列,必须是阻塞队列
                        private final BlockingQueue<Runnable> workQueue;
                        // 工作线程集合,存放线程池中所有的(活跃的)工作线程,只有在持有全局锁mainLock的前提下才能访问此集合
                        private final HashSet<Worker> workers = new HashSet<>();
    
                        // 全局锁
                        private final ReentrantLock mainLock = new ReentrantLock();
                         // awaitTermination方法使用的等待条件变量
                         private final Condition termination = mainLock.newCondition();
                         // 记录峰值线程数
                         private int largestPoolSize;
    
                         // 记录已经成功执行完毕的任务数
                        private long completedTaskCount;
    
                         // 线程工厂,用于创建新的线程实例
                         private volatile ThreadFactory threadFactory;
                         // 拒绝执行处理器,对应不同的拒绝策略
                        private volatile RejectedExecutionHandler handler;
    
                         // 空闲线程等待任务的时间周期,单位是纳秒
                         private volatile long keepAliveTime;
    
                         // 是否允许核心线程超时,如果为true则keepAliveTime对核心线程也生效
                         private volatile boolean allowCoreThreadTimeOut;
    
                         // 核心线程数
                         private volatile int corePoolSize;
                         // 线程池容量
                         private volatile int maximumPoolSize;
                          // 省略其他代码
                 } 
            下面看参数列表最长的构造函数:
             public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
                     if (corePoolSize < 0 ||
                          maximumPoolSize <= 0 ||
                          maximumPoolSize < corePoolSize ||
                          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;   
               }
             可以自定义核心线程数、线程池容量(最大线程数)、空闲线程等待任务周期、任务队列、线程工厂、拒绝策略。
             下面简单分析一下每个参数的含义和作用:

                      corePoolSize:int类型,核心线程数量。

                      maximumPoolSize:int类型,最大线程数量,也就是线程池的容量。

                      keepAliveTime:long类型,线程空闲等待时间,也和工作线程的生命周期有关。

                      unitTimeUnit类型,keepAliveTime参数的时间单位,实际上keepAliveTime最终会转化为纳秒。

                      workQueueBlockingQueue<Runnable>类型,等待队列或者叫任务队列。

                      threadFactoryThreadFactory类型,线程工厂,用于创建工作线程(包括核心线程和非核心线程),默认使用

                      Executors.defaultThreadFactory()作为内建线程工厂实例,一般自定义线程工厂才能更好地跟踪工作线程。

                      handlerRejectedExecutionHandler类型,线程池的拒绝执行处理器,更多时候称为拒绝策略,

                      拒绝策略执行的时机是当阻塞队列已满、没有空闲的线程(包括核心线程和非核心线程)并且继续提交任务。

                      提供了4种内建的拒绝策略实现:

                               AbortPolicy:直接拒绝策略,也就是不会执行任务,直接抛出RejectedExecutionException,这是默认的拒绝策略

                               DiscardPolicy:抛弃策略,也就是直接忽略提交的任务(通俗来说就是空实现)。

                               DiscardOldestPolicy:抛弃最老任务策略,也就是通过poll()方法取出任务队列队头的任务抛弃,然后执行当前提交的任务。

                               CallerRunsPolicy:调用者执行策略,也就是当前调用Executor#execute()的线程直接调用任务Runnable#run()

                               一般不希望任务丢失会选用这种策略,但从实际角度来看,原来的异步调用意图会退化为同步调用

 

学习:https://www.cnblogs.com/throwable/p/13574306.html

posted @ 2020-09-02 10:22  小窝蜗  阅读(191)  评论(0编辑  收藏  举报