1 说明

下面如果有贴出源码,对应的源码是JDK8

主要的源码类

java.util.concurrent.ThreadPoolExecutor、

java.util.concurrent.ThreadPoolExecutor.Worker

java.util.concurrent.AbstractExecutorService

1.1类继承图

2 线程池的状态

3 源码分析

3.1完整的线程池构造方法

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

3.2 ctl

内部有重要的成员变量ctl,类型是AtomicInteger,低29位表示线程池中线程数,通过高3位表示线程池的运行状态

COUNT_BITS的值是29

1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务;

2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务;

3、STOP : 1 << COUNT_BITS,即高3位为001;

4、TIDYING : 2 << COUNT_BITS,即高3位为010, 所有的任务都已经终止;

5、TERMINATED: 3 << COUNT_BITS,即高3位为011, terminated()方法已经执行完成

3.3 任务的执行

execute --> addWorker --> Thread.start --> (Thread.run) --> runTask --> getTask

3.3.1 execute(Runnable command)

大致分三个步骤

1、当前运行的线程数量是否小于corePoolSize,直接尝试addWorker()

2、往阻塞队列里面放入Runnable任务

3、如果队列已经满了,直接尝试addWorker()

3.3.2 addWorker(Runnable firstTask, boolean core)

1、前置判断线程池的状态

2、通过CAS操作让ctl加1,表示运行线程数增加1个

3、构造一个Worker w,这里要特别注意构造方法里面的这行代码,this.thread = getThreadFactory().newThread(this),可以看到构造方法内,有一个Thread对象,其使用了ThreadFactory构造了一个新的线程,并且线程的runable是worker本身。

4、执行w.thread.start(),也就是说,当该线程被运行时,Worker中的run方法会被执行

3.3.3 runWorker(Worker w)

通过循环调用getTask()获取要执行的任务task

beforeExecute

task.run()

afterExecute

3.3.4 getTask()

直接贴源码了

private Runnable getTask() {
boolean timedOut = false; // 是否最后的 poll() 超时了?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
} int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // worker是否需要被淘汰 if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 这里会让线程的数量记录减,后面的return null,会导致runWorker没有获取到数据而让run()方法走到尽头,最终当前线程结束
if (compareAndDecrementWorkerCount(c))
return null;
continue;
} try {
// 如果需要回收一部分线程,那么超时时间keepAliveTime后拿不到就数据就继续循环调用,就可以在下一次循环的时候进行线程结束回收了;否则一直阻塞下去
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}

4 任务执行,带返回值的

直接贴源码了

public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}

代码比较简单,把任务封装成一个既实现Runnable, 也实现Future的接口,这个时候就可以调用execute()进行实现了

5 参考资料

https://blog.csdn.net/programmer_at/article/details/79799267

https://blog.csdn.net/liuzhixiong_521/article/details/87856121

Java线程池源码及原理的更多相关文章

  1. java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  2. Java线程池源码解析

    线程池 假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率.线程池能够对线程进行统一的分 ...

  3. 【图灵学院10】高并发之java线程池源码分析

    1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...

  4. java线程池源码的理解

    线程池 新建线程和切换线程的开销太大了,使用线程池可以节省系统资源. 线程池的关键类:ThreadPoolExecutor. 该类中包含了大量的多线程与并发处理工具,包括ReentrantLock.A ...

  5. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  6. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  7. 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记

    1.ScheduledThreadPoolExecutor 整体结构剖析. 1.1类图介绍 根据上面类图图可以看到Executor其实是一个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池 ...

  8. Java并发编程中线程池源码分析及使用

    当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnb ...

  9. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

随机推荐

  1. WPF ListView的使用

    <Window x:Class="XamlTest.Window14"        xmlns="http://schemas.microsoft.com/win ...

  2. OnPropertyChanged的使用

    #region INotifyPropertyChanged         public event PropertyChangedEventHandler PropertyChanged;     ...

  3. linux C 内存管理方式之半动态

    看到半动态申请内存,第一反应这是什么鬼? 实际上半动态内存申请很容易理解,在GNU C中使用alloca函数来实现 #include <stdlib.h> void *alloca (si ...

  4. php 二维数组key初始化从0开始

    这个是一个二维数组 array(2) { [1]=> array(2) { ["sourcesid"]=> int(1) ["addusernum" ...

  5. 【Repo】推送一个已有的代码到新的 gerrit 服务器

    1.指定项目代码库中迭代列出全部ProductList(.git)到pro.log文件中 repo forall -c 'echo $REPO_PROJECT' | tee pro.log 命令解读: ...

  6. Win10《芒果TV》商店版双十一独家大礼,每日前100名用户免费领取7天VIP

    为答谢大家对Win10<芒果TV>商店版一年以来一如既往的支持,2016年11月1日-11月30日期间,每天登录<芒果TV>UWP版(最新版本v3.1.3)的前100位用户可领 ...

  7. 无法解决 equal to 操作中 "SQL_Latin1_General_CP1_CI_AS" 和 "Chinese_PRC_CI_AS" 之间的排序规则冲突。

    无法解决 equal to 操作中 "SQL_Latin1_General_CP1_CI_AS" 和 "Chinese_PRC_CI_AS" 之间的排序规则冲突 ...

  8. Silverlight ItemsControl详细解析+解惑

    Silverlight最强大的地方就在于定义控件了,Silverlight提供了非常灵活和高效的控件定义方式,几乎可以实现任何复杂的控件实现,对于快速开发应用程序有着重要的意义.在Silverligh ...

  9. STL函数static void (* set_malloc_handler(void (*f)()))()与函数指针解析

    在C++ STL的SGI实现版本中,一级空间配置器class __malloc_alloc_template中有一个静态函数的实现如下: static void (*set_malloc_handle ...

  10. HTML连载9-video标签的第二种格式&audio标签

    一.video第二种格式 1.背景:由于视频数据非常重要,所以五大浏览器厂商都不愿意支持别人的视频格式,所以导致了没有一种视频格式是所有浏览器都支持的.这个时候W3C为了解决这个问题,所以推出了第二种 ...