线程池的设计与原理解析(一)之---基础入门

什么是线程池
在 Java 中,如果每个请求到达就创建一个新线程,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。如果在一个 Jvm 里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足为了解决这个问题,就有了线程池的概念,线程池的核心逻辑是提前创建好若干个线程放在一个容器中。如果有任务需要处理,则将任务直接分配给线程池中的线程来执行就行,任务处理完以后这个线程不会被销毁,而是等待后续分配任务。同时通过线程池来重复管理线程还可以避免创建大量线程增加开销。
工作原理如下图所示:
在这里插入图片描述

线程池的优势
1.降低资源消耗
2.提供响应速度
3.提供线程的可管理性
线程池的使用
在Java中,由 Executors 里面提供了几个线程池的工厂方法。
1.newFixedThreadPool:该方式返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。
2.newSingleThreadExecutor: 创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中
3.newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在 60 秒后自动回收
4.newScheduledThreadPool: 创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。
但是在实际的生产环境中,却并不使用Executors提供的方式进行创建线程池。因为这几个均有响应的弊端
newFixedThreadPool,newSingleThreadExecutor,.newScheduledThreadPool 这些创建的方式中,他们的Queue的最大值为Integer.MAX_VALUE;即,在等待队列时,即队列无限大;
在这里插入图片描述

newCachedThreadPool这些方式创建的,他们的线程池的数量最大值为Integer.MAX_VALUE,导致OOM的问题;
在这里插入图片描述
所以一般,我们都是自己重写ThreadPoolExecutor()方法,自定义参数来进行创建线程池。
接下来,我们就来学习ThreadPoolExecutor()该方法

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

这里的七大参数代表的含义分别为:
1.corePoolSize: 核心线程数量
2.maximumPoolSize: 最大线程限制
3.keepAliveTime: 空余线程存活时间
4.unit : 时间单位
5.workQueue: 任务队列
6.threadFactory: 线程工厂
7.handler: 拒绝策略, 默认为抛出异常。

接下来,就针对ThreadPoolExecutor这个类的源码进行相关的学习。
认识一些常量和变量及方法相关的含义
1.ctl :高三位表示当前线程池允许状态; 除去高三位之后的低位: 表示当前线程池中所拥有的线程数量

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

2.使用CAS 让ctl 值 +1 成功返回true,失败返回 false

  private boolean compareAndIncrementWorkerCount(int expect) {
    
    
        return ctl.compareAndSet(expect, expect + 1);
    }

3.任务队列,当线程池中的线程达到核心线程数量时,再提交任务,就会直接提交到 workQueue workQueue 类型: ArrayBrokingQueue LinkedBrokingQueue 同步队列

   private final BlockingQueue<Runnable> workQueue;

4.线程池全局锁 增加worker 减少worker 需要持有 mainLock 修改线程池运行状态时,也需要

 private final ReentrantLock mainLock = new ReentrantLock();

5.线程池中真正存放 worker --> thread 的地方

 private final HashSet<Worker> workers = new HashSet<Worker>();

6.记录线程池生命中期内 线程数最大值

 private int largestPoolSize;

7.记录线程池所完成任务总数,当worker 退出时,会将worker 完成的任务累计到 completedTaskCount

 private long completedTaskCount;

8.创建线程时,会使用线程工厂

private volatile ThreadFactory threadFactory;

9.拒绝策略,juc包中提供了4种方式,默认采用 抛出异常的方式

 private volatile RejectedExecutionHandler handler;

10空闲线程存款时间,当 allowCoreThreadTimeOut == false时, 会维护核心线程数量内的线程存活,超出部分会被超时;

private volatile long keepAliveTime;

11.控制核心线程数量内的线程是否可以被回收

 private volatile boolean allowCoreThreadTimeOut;
  1. 核心线程数量限制
private volatile int corePoolSize;

13.线程池最大线程限制

 private volatile int maximumPoolSize;
  1. 缺省拒绝策略,采用的是 AbortPolicy 抛出异常的方式
  private static final RejectedExecutionHandler defaultHandler =
            new AbortPolicy();

15.当worker启动时,会执行run()

 public void run() {
    
    
            // 核心方法
            runWorker(this);
        }
  1. 判断当前的worker是否被独占 0表示未被占用 1表示被占用了
  protected boolean isHeldExclusively() {
    
    
            return getState() != 0;
        }

17.尝试去占用worker的独占锁

protected boolean tryAcquire(int unused) {
    
    
            //使用CAS修改 AQS中的status,期望值为0(未占用)
            if (compareAndSetState(0, 1)) {
    
    
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

Guess you like

Origin blog.csdn.net/qq_35529931/article/details/119841121