Java线程池工作原理
前言
当项目中有频繁创建线程的场景时,往往会用到线程池来提高效率。所以,线程池在项目开发过程中的出场率是很高的。
那线程池是怎么工作的呢?它什么时候创建线程对象,如何保证线程安全...
什么时候创建线程对象
当实例化线程池对象时,并没有预先创建corePoolSize个线程对象,而是在调用execute或submit提交任务时,才会创建线程对象。
工作流程
public void execute(Runnable command) {
        int c = ctl.get();
        // 1. 如果核心线程数没有用完,则让核心线程执行任务
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 2. 核心线程用完后,尝试添加到等待队列
        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);
        }
        // 3. 等待队列满后,尝试创建临时线程执行任务;
        // 如果创建临时线程失败,即达到了最大线程数,则采用拒绝策略处理
        else if (!addWorker(command, false))
            reject(command);
    }
如何保存线程池状态
// 线程池采用状态压缩的思想,通过 32 个 bit 位来存储线程池状态和工作线程数量
// 其中,高 3 位存储线程池状态,低 29 位存储工作线程数量
// 表示工作线程数量位数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池最大容量
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// 通过线程安全的 atomic 对象来存储线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }
如何保证线程安全
1、对于一些需要线程共享的成员变量,通过volatile修饰
2、线程池状态和工作线程数的修改通过AtomicInteger类自带的线程安全方法实现
3、工作线程Worker通过线程不安全的HashSet存储,每次操作HashSet时都通过一个可重入锁ReentrantLock保证线程安全,ReentrantLock还用来保证访问一些线程不安全的变量,比如
/**
 * Tracks largest attained pool size. Accessed only under
 * mainLock.
 */
private int largestPoolSize;
/**
 * Counter for completed tasks. Updated only on termination of
 * worker threads. Accessed only under mainLock.
 */
private long completedTaskCount;
4、Worker实现了AQS类,保证线程安全地操作工作线程Worker。Worker通过设置AQS#state来实现加锁和释放锁,当state == 0时,表示没有上锁;当state == 1时,表示上锁,通过CAS方式实现线程安全
protected boolean tryAcquire(int unused) {
	if (compareAndSetState(0, 1)) {
		setExclusiveOwnerThread(Thread.currentThread());
		return true;
	}
	return false;
}
public void lock()        { acquire(1); }
public boolean tryLock()  { return tryAcquire(1); }
public void unlock()      { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
内置线程池
1、SingleThreadPool
单线程池,只使用一个线程执行任务,阻塞队列LinkedBlockingQueue最大容量为Integer.MAX_VALUE。
public static ExecutorService newSingleThreadExecutor() {
	return new FinalizableDelegatedExecutorService
			(new ThreadPoolExecutor(1, 1,
			0L, TimeUnit.MILLISECONDS,
			new LinkedBlockingQueue<Runnable>()));
}
2、FixedThreadPool
固定大小的线程池,当创建的线程数量达到maxPoolSize,就不再创建线程,阻塞队列LinkedBlockingQueue最大容量为Integer.MAX_VALUE。
public static ExecutorService newFixedThreadPool(int nThreads) {
	return new ThreadPoolExecutor(nThreads, nThreads,
					0L, TimeUnit.MILLISECONDS,
					new LinkedBlockingQueue<Runnable>());
}
3、CachedThreadPool
缓存线程池,核心线程数量为0,所以任务都通过创建临时线程来执行,临时线程空闲回收时间为60秒,当线程池空闲时,临时线程都会被回收,不耗费资源。
阻塞队列SynchronousQueue不会缓存任务,也就是说,新任务进来后会直接被调度执行,如果没有可用的线程了,则会创建新的线程,如果线程数达到maxPoolSize,即Integer.MAX_VALUE,就执行拒绝策略。
public static ExecutorService newCachedThreadPool() {
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
					60L, TimeUnit.SECONDS,
					new SynchronousQueue<Runnable>());
}
4、ScheduleThreadPool
定时调度线程池,通过DelayedWorkQueue
public ScheduledThreadPoolExecutor(int corePoolSize) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		new DelayedWorkQueue());
}
5、WorkStealingThreadPool
工作窃取线程池,当一个处理器忙时,空闲的处理器可以窃取该处理器后续的任务执行。通过ForkJoinPool实现,可设置支持的并行级别。
public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
}
												
											Java线程池工作原理的更多相关文章
- Java线程池的原理及几类线程池的介绍
		
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
 - Java 线程池的原理与实现 (转)
		
最近在学习线程池.内存控制等关于提高程序运行性能方面的编程技术,在网上看到有一哥们写得不错,故和大家一起分享. [分享]Java 线程池的原理与实现 这几天主要是狂看源程序,在弥补了一些以前知 ...
 - Java线程池实现原理及其在美团业务中的实践
		
本文转载自Java线程池实现原理及其在美团业务中的实践 导语 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流.使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器.J.U.C提供 ...
 - 我眼中的java线程池实现原理
		
最近在看java线程池实现方面的源码,在此做个小结,因为网上关于线程池源码分析的博客挺多的,我也不打算重复造轮子啦,仅仅用纯语言描述的方式做做总结啦! 个人认为要想理解清楚java线程池实现原理,明白 ...
 - 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
		
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
 - Java 线程池(ThreadPoolExecutor)原理分析与使用
		
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
 - Java 线程池(ThreadPoolExecutor)原理解析
		
在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...
 - Java线程池实现原理与技术(ThreadPoolExecutor、Executors)
		
本文将通过实现一个简易的线程池理解线程池的原理,以及介绍JDK中自带的线程池ThreadPoolExecutor和Executor框架. 1.无限制线程的缺陷 多线程的软件设计方法确实可以最大限度地发 ...
 - 深入浅出JAVA线程池使用原理1
		
前言: Java中的线程池是并发框架中运用最多的,几乎所有需要异步或并发执行任务的程序都可以使用线程池,线程池主要有三个好处: 1.降低资源消耗:可以重复使用已经创建的线程降低线程创建和销毁带来的消耗 ...
 
随机推荐
- [HNOI2011]XOR和路径 题解
			
设 \(f(x)\) 表示从 \(x\) 节点走到 \(n\) 的期望.有 $$f(x)=\sum_{{x,y}}\frac{f(y)\oplus w(x,y)}{{\rm deg}(x)}$$ 由于 ...
 - 【贪心+排序】凌乱的yyy / 线段覆盖 luogu-1803
			
题目描述 现在各大oj上有n个比赛,每个比赛的开始.结束的时间点是知道的. yyy认为,参加越多的比赛,noip就能考的越好(假的) 所以,他想知道他最多能参加几个比赛. 由于yyy是蒟蒻,如果要参加 ...
 - 第九篇 -- 对数据库mysql进行连接并压测(二)
			
上一节介绍了对mysql查询语句的压测,这一节来进一步的了解. 还是先把数据库的图放上来. 接下来打开Jmeter. 1. 回顾一下上一节学的查询语句 JDBC Request配置 结果 2. 条件查 ...
 - 在Python中执行普通除法
			
如果希望Python只执行普通的除法,那么可以在程序前加上以下语句: 1 from _future_ import division 如果通过命令行(比如在Linux系统上)运行Python,可以使用 ...
 - JBoss 5.x/6.x 反序列化漏洞(CVE-2017-12149)
			
检测
 - python编写DDoS攻击脚本
			
python编写DDoS攻击脚本 一.什么是DDoS攻击 DDoS攻击就是分布式的拒绝服务攻击,DDoS攻击手段是在传统的DoS攻击基础之上产生的一类攻击方式.单一的DoS攻击一般是采用一对一方式的, ...
 - bugku-misc 9-16
			
宽带信息泄露 拿到一个bin后缀的文件,知道是一个二进制文件,但是题目提到的和宽带有关,flag就是用户名.可以使用RoutePassView这个工具,使用的时候记得关闭杀毒软件. 工具可以在这里下载 ...
 - Java线程基础及多线程的实现
			
一.进程和线程 1.进程:正在运行的程序 是系统进行资源分配和调用的独立单位 每一个进程都有它自己的内存空间和系统资源 2.线程是进程中的单个顺序控制流,是一条执行路径 ...
 - 【C++】使用 libass,完成 Direct3D 11 下的字幕渲染
			
前言 前段时间曾经写过一个视频播放器:https://www.cnblogs.com/judgeou/p/14746051.html . 然而这个播放器却无法显示出外挂或者内封的字幕,这里要稍微解释一 ...
 - SpringBoot开发十-开发登录,退出功能
			
需求介绍-开发登录,退出功能 访问登录页面:点击头部区域的链接打开登录页面 登录: 验证账号,密码,验证码 成功时生成登录凭证发放给客户端,失败时跳转回登录页面 退出: 将登录状态修改为失效的状态 跳 ...