一、Java构建线程的方式

      1.继承Thread(几乎不用)

      2.实现Runnable

      3.实现Callable

      4.线程池(Java提供了构建线程池的方式)Java提供了Executors  常见线程池,(规范中不允许使用这种方式,对象成的控制力度比较低)

            推荐手动创建

    二、线程池的7个参数

      

 

    三、线程池执行流程

      

    四、线程池表示属性

 

                            

 

 

      状态之间的转换

                                      

 

    五、线程池的execute方法(源码分析)

    引用自https://zhuanlan.zhihu.com/p/398846689(源码分析比较详细)

// 初始化线程池的状态和当前线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 线程池的状态和数量分别由一个32位的整形前3位和后29位表示
// 这个COUNT_BITS=29
private static final int COUNT_BITS = Integer.SIZE - 3;
// COUNT_MASK二进制数值:0001 1111 1111 1111 1111 1111 1111 1111
// 29个1,基本上是用来做位运算计算线程数量的
// 还有一点就是,这个数代表最大线程数,因为29个位置都是1
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 下面的5个状态,只需要关注二进制的前3位即可
// RUNNING = 1110 0000 0000 0000 0000 0000 0000 0000
// -1的话,需要转换成补码才能向右移29位
private static final int RUNNING    = -1 << COUNT_BITS;
// SHUTDOWN = 0000 0000 0000 0000 0000 0000 0000 0000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// STOP = 0010 0000 0000 0000 0000 0000 0000 0000
private static final int STOP       =  1 << COUNT_BITS;
// TIDYING = 0100 0000 0000 0000 0000 0000 0000 0000
private static final int TIDYING    =  2 << COUNT_BITS;
// TERMINATED = 0110 0000 0000 0000 0000 0000 0000 0000
private static final int TERMINATED =  3 << COUNT_BITS;
// 计算c这个数值和1110 0000 0000 0000 0000 0000 0000 0000相与的结果,其实就是计算线程池的状态
private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
// 计算当前线程池的线程数量;c与0001 1111 1111 1111 1111 1111 1111 1111相与
private static int workerCountOf(int c)  { return c & COUNT_MASK; }
// rs为线程状态,wc是线程数量;一般使用这个方法计算ctl值,也就是状态和数量共同值
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池对象初始化时执行ctlOf(RUNNING,0)返回一个int值作为ctl的初始化值。RUNNING是前3位全部为1,与0执行位运算|,这个0就是初始化的时候是0个线程,得到的结果如下。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }

  • 添加任务
public void execute(Runnable command) {
    // 添加的任务不能为空
    if (command == null)
        throw new NullPointerException();
    // ctl是一个32位的整形值,前3为代表线程池的状态,后29位代表当前线程池中线程的数量
    int c = ctl.get();
    // workerCountOf(c)这个方法是通过位运算计算当前线程池线程数量;当线程数量小于核心线程数,那么执行添加工作线程的操作
    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);
}
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        // 开启自旋;先获取线程池状态及数量
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            // 如果c=[SHUTDOWN | STOP | TIDYING | TERMINATED]其中之一,并且c = [STOP | TIDYING | TERMINATED] 或者 任务不为空 或者等待队列为空,就返回false
            //翻译成人话,就是
            // 1.如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,那么就直接创建失败
            // 2.如果线程池的状态处于SHUTDOWN,并且任务不为空的话,那么也创建失败。
            // 3.如果线程池的状态处于SHUTDOWN,并且等待队列为空,同样创建失败。
            // 上述条件下,满足其一,都会创建新线程失败,走向拒绝策略。
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;
            // 走到这里,说明此刻线程池的状态处于RUNNING
            for (;;) {
            // 1.如果是创建核心线程,那么就比较一下当前线程数量是否大于等于核心线程数
            // 2.如果是创建非核心线程,那么比较一下当前线程数是否大于等于最大线程数
            // 如果上述条件确实满足,说明就不能创建工作线程了,那就要创建失败。
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                // 此时需要通过CAS的方式实现线程数+1,这个操作必须满足原子性。
                if (compareAndIncrementWorkerCount(c))
                // 如果自增成功,说明拿到了创建工作线程的通行证,那么就直接跳出双重循环,进入下一关去真正地创建工作线程。
                    break retry;
                //没有拿到通行证的话,就好好地自旋
                // 重新读取状态
                c = ctl.get();  // Re-read ctl
                // 如果当前状态处于[SHUTDOWN | STOP | TIDYING | TERMINATED]中,那么最终会返回false,创建工作线程失败
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
                // 否则重新内部自旋直至满足以下情况
                // 1.线程数量超过限制,创建失败。
                // 2.自增成功,拿到通行证,进入下一步创建工作线程
                // 3.线程池状态发生变化,最终创建失败。
            }
        }
// 有通行证的才能执行到这里
        // 下面新创建的工作线程是否开始运行的标识
        boolean workerStarted = false;
        // 下面新创建的工作线程是否被添加到工作线程集合中
        boolean workerAdded = false;
        Worker w = null;
        try {
        // 创建一个工作线程;
        // 一个work绑定一个线程,创建work的同时,会创建一个对应的线程
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
            // 获取一个重入锁
                final ReentrantLock mainLock = this.mainLock;
                // 上锁;
                //如果看不明白的话,就把这两行代码替换成synchronized (this)
                mainLock.lock();
                // 解锁前的逻辑都不存在线程安全的问题
                try {
                    // 重新检查线程池状态
                    int c = ctl.get();
                    // 1.如果线程池状态位RUNNUING
                    // 2.如果线程池状态处于[RUNNING | SHUTDOWN],并且任务为null的情况
                    // 那么都可以把当前工作线程放进工作线程集合中
                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        // 确保刚刚创建出来的线程没有还没有运行。其实就是没有执行start()方法。这种情况基本不存在。
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 把工作线程放进集合中。
                        // 这个集合是HashSet,在获取重入锁的情况下不存在线程安全问题。
                        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)
            // 1.从集合中移除
            // 2.线程数减1
            // 3.尝试tryTerminate()终止操作,不一定会终止
                addWorkerFailed(w);
        }
        // 返回新创建的工作线程是否启动的结果
        // 工作线程成功启动也证明成功地创建新的工作线程。
        return workerStarted;
    }
// 1.如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,那么就直接创建失败
            // 2.如果线程池的状态处于SHUTDOWN,并且任务不为空的话,那么也创建失败。
            // 3.如果线程池的状态处于SHUTDOWN,并且等待队列为空,同样创建失败。
            // 上述条件下,满足其一,都会创建新线程失败,走向拒绝策略。
if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

上述代码需要仔细解释一下。

  • 首先如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,是不能再创建新的线程了,也不会接受新的任务,直接走向拒绝策略
  • 其次如果是处于SHUTDOWN状态的话,如果等待队列不为空的话,可以接受创建新的工作线程来帮忙,但是不能接受新的任务。
  • 最后,如果处于SHUTDOWN状态,并且等待队列中没有任务的话,那么不接受创建新的线程,更不接受新的任务。

所以SHUTDOWN属于软关闭状态。

// 1.如果是创建核心线程,那么就比较一下当前线程数量是否大于等于核心线程数
            // 2.如果是创建非核心线程,那么比较一下当前线程数是否大于等于最大线程数
            // 如果上述条件确实满足,说明就不能创建工作线程了,那就要创建失败。
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;

其实addWorker(Runnable firstTask, boolean core)这个core的作用就是用来确定是否是创建核心线程,根据这个值来间接判断对应的线程数量是否超标了。并且在后续创建工作线程的代码中并没有起到作用,所以可以判断,核心线程与非核心线程并没有本质上的差别,在创建工作线程时并不会标记某个线程是核心线程。

// 1.如果线程池状态位RUNNUING
// 2.如果线程池状态处于[RUNNING | SHUTDOWN],并且任务为null的情况
// 那么都可以把当前工作线程放进工作线程集合中
if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) 

上面的代码强调两点:

  • RUNNING状态可以创建工作线程和处理任务
  • SHUTDOWN状态,等待队列还有任务的话,只允许创建新的线程帮忙处理剩下的任务,不能添加新任务。
 

 

 

    

 

 

 

 

  

 

posted on 2022-04-12 14:36  迎新  阅读(92)  评论(0编辑  收藏  举报