乐趣区

关于多线程:多线程线程池源码2

时隔上一篇技术文章更新差不多有 3 个星期了,起因的话在上一篇文章中写啦。废话不多说,开始咱们的线程池源码的第二轮浏览。

回顾

简略回顾下上一篇线程池源码中波及的两个办法,一个是execute() 执行工作的入口,还有一个是addWorker() 最艰深地了解就是是否须要增加新线程。而在addWoker() 的开端有这样一段代码

if (workerAdded) {t.start();
    workerStarted = true;
}

显著地看到这里通过 start() 办法开启了多线程,而如果想要看线程的执行逻辑,就须要去到对应类中查看 run 办法,这里的 t 就是Worker 类外面的一个成员变量,所以 重点要看Worker 类中的run() 办法。

runWorker()

run() 办法的源码如图所示,最初是到了runWorker()

间接来看 runWorker 的源码

  1. 开始是一个循环,要么执行 worker 自带的第一个工作(firstTask),要么通过getTask() 获取工作
  2. 有工作首先得保障线程池是失常的,以下两种状况均调用 wt.interrupt() 线程设置中断标记位

    1. 线程池处于 STOP 状态,也就是不承受新工作,也不执行队列中的工作
    2. 如果线程的标记位曾经为 true,那么分明标记位,此时的线程池状态为 STOP 状态,这里看起来可能比拟顺当,有了第一种状况为什么还要第二种,不了解的能够先略过,前面会讲的。
  3. 失常状况下是调用beforeExecute()afterExecute() 包裹者task.run()

看一下是如何 自定义前置和后置执行逻辑

因为是换电脑写了,所以例子可能和前一篇文章的不齐全一样,然而表白的是同一个意思

public class ThreadPoolExamples {public static void main(String[] args) {ThreadPoolExecutor executor = new MyThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

        MyThread myThread = new MyThread();
        executor.execute(myThread);

    }
}


class MyThread extends Thread {
    @Override
    public void run() {System.out.println(Thread.currentThread().getName() + "is running");
    }
}

class MyThreadPoolExecutor extends ThreadPoolExecutor {
    @Override
    protected void beforeExecute(Thread t, Runnable r) {System.out.println("【" + Thread.currentThread().getName() + "custom before execute】");
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {System.out.println("【" + Thread.currentThread().getName() + "is done】");
    }

    MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }
}

首先就是要创立本人的MyThreadPoolExecutor 类,继承ThreadPoolExecutor,而后重写beforeExecute()afterExecute() 定义本人的逻辑即可,看下测试后果

能够看到前置和后置都曾经依照既定的逻辑在运行了,乏味的是,22 分钟过来了(不要问我为什么这么久,拿外卖吃货色去了)线程池还是没有停,为什么会这样呢。

留神后面剖析runWorker() 第一步的时候是一个循环,而后通过 firstTask 或者getTask() 获取工作,如果两种形式都获取不到工作,线程池就应该退出,看来神秘在getTask() 中。

getTask()

次要目标顾名思义就是获取到须要执行的工作,间接看源码

  1. 一进来也是一个死循环,能够先聚焦 什么时候会退出循环 ,必定是 不失常的状况 下会退出

    1. 当线程池状态不处于 RUNNING 或者 SHUTDOWN 的时候,或者是当线程处于 SHUTDOWN 然而工作队列中没有工作
    2. 当 wc 大于最大线程数并且工作队列为空的时候,或者当 wc 大于外围线程数并且 timedOut 为 true 并且外围队列为空的时候,或者如果设置了 allowCoreThreadTimeOut,并且 wc > 1 或者外围队列为空的时候
  2. 除了不失常的状况,接下来就是从工作队列中获取工作,不过是依据 timed 的来决定是用poll() 还是take()
  3. 如果能取出工作,就间接返回工作;如果没有工作,要么超时设置 timedOut 为 ture,要么是抛出异样重置 timedOut 为 false。

能够看到,只有上述不失常的状况下退出循环,工作返回 null,进而导致runWorker() 中的 while 循环退出,最初整个线程池敞开。否则都是会始终在getTask() 这里死循环。

到这里,为什么说线程池可能节俭资源呢,是因为其实它创立的线程的耗费只是体现在了 Worker 类的创立中,把其它要实现的工作放在工作队列外面,而后getTask() 获取工作,最初执行工作(调用task.run())

回绝策略

这个是在最开始execute() 的时候调用的

具体是如何请看上面的动图

能够看到,最初是调用了RejectedExecutionHandler 接口中的rejectedExecution(Runnable r, ThreadPoolExecutor executor); 办法,而后默认是有 4 种实现形式

有凋谢的接口,那必定是能自定义实现类的

class MyRejectedExecutionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("task is rejected");
    }
}

该说的办法也根本都说了,用到的工作队列(BlockingQueue)在前面会另说,下一篇文章就总结下 jdk 的线程池啦!

创作不易,如果对你有帮忙,欢送点赞,珍藏和分享啦!

上面是集体公众号,有趣味的能够关注一下,说不定就是你的宝藏公众号哦,根本 2,3 天 1 更技术文章!!!

退出移动版