线程池底层原理分析

一、线程池

  1. public ThreadPoolExecutor(int corePoolSize, // 核心线程数
  2. int maximumPoolSize, // 最大线程数
  3. long keepAliveTime, // 非核心线程存活时间
  4. TimeUnit unit, // 非核心线程存活时间单位
  5. BlockingQueue<Runnable> workQueue, // 队列
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler) {// 拒绝策略
  8. if (corePoolSize < 0 ||
  9. maximumPoolSize <= 0 ||
  10. maximumPoolSize < corePoolSize ||
  11. keepAliveTime < 0)
  12. throw new IllegalArgumentException();
  13. if (workQueue == null || threadFactory == null || handler == null)
  14. throw new NullPointerException();
  15. this.corePoolSize = corePoolSize;
  16. this.maximumPoolSize = maximumPoolSize;
  17. this.workQueue = workQueue;
  18. this.keepAliveTime = unit.toNanos(keepAliveTime);
  19. this.threadFactory = threadFactory;
  20. this.handler = handler;
  21. }

二、线程池底层执行原理

当有一些n个任务需要处理时,此时会判断 n是否大于核心线程数,如果小于核心线程数,则会创建n个线程来处理这n个任务,如果n大于核心线程数,则会把核心线程处理不了的任务放入阻塞队列中进行等待,如果此时阻塞队列满了后,会判断此时任务数是否大于最大线程数,如果n小于最大线程数,则会创建线程来处理核心线程和阻塞队列存不下的任务,即创建那么多的线程,这些线程如果过了存活时间,则会被回收;如果n大于最大线程数,那么那些多出来的任务将不会得到处理,直接进行拒绝策略执行。

三、线程池中执行顺序

  • 提交任务的顺序

    • 核心线程区 —> 阻塞队列 —> 非核心线程区
  • 执行任务

    • 核心线程区 —> 非核心线程区 —> 阻塞队列

四、线程池创建方式以及各自优缺点

  1. ExecutorService executorService = Executors.newFixedThreadPool(10);
  2. public static ExecutorService newFixedThreadPool(int nThreads) {
  3. return new ThreadPoolExecutor(nThreads, nThreads,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>());
  6. }

缺点:由于阻塞队列用的是LinkedBlockingQueue,LinkedBlockingQueue的链表长度为int的最大值(2^31-1),

如果任务量特别大时,容易导致OOM(内存溢出)

  1. public static ExecutorService newCachedThreadPool() {
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  3. 60L, TimeUnit.SECONDS,
  4. new SynchronousQueue<Runnable>());
  5. }

缺点:由于最大线程数为int的最大值,如果任务量特别大时,会创建大量的线程,占用非常大的cpu资源