
JUC源码解读
文章平均质量分 87
持续改进,坦诚合作!我愿与你一起学习和探讨,排除万难,领略作者匠心思维
徐同学呀
致力于java底层、源码、中间件、后端领域学习和探索。热爱源码,热爱生活,持续改进,坦诚合作!
展开
-
关于ReentrantReadWriteLock,首个获取读锁的线程单独记录问题讨论(该如何更好的阅读源码)
`firstReader`表示第一个获取读锁的线程,若当前线程等于`firstReader`,读锁重入时`firstReaderHoldCount+1`,而非首个获取读锁的线程则用一个继承了`ThreadLocal`的内部类`ThreadLocalHoldCounter`给每个线程计数。为什么第一个线程不用`ThreadLocalHoldCounter`计数呢?单独拎出来的意义何在?原创 2021-04-16 00:19:00 · 746 阅读 · 2 评论 -
ConcurrentHashMap源码深度解析(二)(java8)——直呼Doug Lea是真的细(带你参透扩容机制)
扩容是重头戏,看过的人都说难。确实,和java7版本比起来,难度真不是一个量级的。有些细节看着莫名其妙,一想就是好几天,看似想明白也只能算是猜想合理,直呼Doug Lea的心思是真的细啊! 深究细节是费时且痛苦的,欣喜的是,怎么也想不明白的逻辑,发现这次是源码错了!官方JDK!现在市面上普遍都在用java8,怎么可能存在bug呢?原创 2021-03-21 14:44:48 · 2553 阅读 · 19 评论 -
ConcurrentHashMap源码深度解析(一)(java8)——不可不知的基本概念(助你拿下源码事半功倍)
ConcurrentHashMap java1.8数据结构采用数组+链表+红黑树,废弃分段锁`Segement`,进一步降低锁的粒度,可将锁直接加在数组占位节点上。同时发生哈希冲突的节点依然采用链表法,但是加了红黑树进行检索优化,即链表与红黑树互相转化,即使到了极端情况,检索的时间复杂度为O(lgn),相对于O(n)性能提升不少。原创 2021-03-14 23:06:41 · 828 阅读 · 3 评论 -
ConcurrentHashMap 源码深度解析(java7)——原来如此简单
ConcurrentHashMap在java7中的实现有很多值得学习借鉴的地方,比如基本的数据结构数组链表的应用,并发开发,哈希算法等都可以学以致用。而且了解了java7的实现细节,才能更好的明白java8中为什么要做的各种优化? 1. ConcurrentHashMap的数据结构是怎样的? 2. ConcurrentHashMap的容量为什么是2的整数次方? 3. 如何实现的并发安全?是读写分离吗?get需要加锁吗? 4. 哈希冲突体现在哪里,如何解决? 5. 扩容思想是什么,怎么扩容?原创 2021-03-07 10:36:05 · 709 阅读 · 2 评论 -
CopyOnWriteArrayList源码解读——CopyOnWrite思想的利与弊
`CopyOnWriteArrayList`利用`CopyOnWrite`思想,即在写时复制一份副本进行修改,修改完成后,再将新值赋值给旧值,为保证线程安全,需要在所有的写操作加悲观锁或者乐观锁,而读操作不必加锁,这就使得读写分离,读读不互斥,读写不互斥,空间换时间,性能大大提升。原创 2020-12-24 00:07:17 · 874 阅读 · 0 评论 -
FutureTask源码解读——阻塞获取异步计算结果(阻塞、取消、装饰器、适配器、Callable)
FutureTask作为Runnable的子类,它就像是一个装饰器在Runnable异步执行的功能上,又增加了可以获取异步执行状态以及结果的功能: 其内部维护了一个`Callable`类型的成员变量,任务代码会包装成callable,FutureTask直接调用Callable.call()执行任务代码并返回结果。 还维护了一个链表实现的栈,外部获取结果的线程在任务没有执行完前都会被压入栈并阻塞(awaitDone),任务完成唤醒所有阻塞线程(finishCompletion)。原创 2020-11-15 21:29:04 · 860 阅读 · 0 评论 -
ScheduledThreadPoolExecutor源码解读(一)——DelayedWorkQueue高度定制延迟阻塞优先工作队列
工作队列是高度定制化的延迟阻塞队列DelayedWorkQueue,其实现原理和DelayQueue基本一样,核心数据结构是二叉最小堆的优先队列,队列满时会自动扩容,所以offer操作永远不会阻塞,maximumPoolSize也就用不上了,所以线程池中永远会保持至多有corePoolSize个工作线程正在运行。原创 2020-11-08 13:25:15 · 1088 阅读 · 0 评论 -
ScheduledThreadPoolExecutor源码解读(二)——ScheduledFutureTask时间调度执行任务(延迟执行、周期性执行)
延迟阻塞队列`DelayedWorkQueue`中放的元素是`ScheduledFutureTask`,提交的任务被包装成`ScheduledFutureTask`放进工作队列,`Woker`工作线程消费工作队列中的任务,即调用`ScheduledFutureTask.run()`,`ScheduledFutureTask`又调用任务的`run()`,这点和`ThreadPoolExecutor`差不多,而`ScheduledThreadPoolExecutor`是如何实现按时间调度的呢?原创 2020-11-08 09:24:49 · 1379 阅读 · 2 评论 -
ThreadPoolExecutor源码解读(四)——如何正确使用线程池(总结坑点+核心参数调优)
1. 不要使用无界工作队列,否则高负载情况容易发生OOM。 2. 任务代码需要自行try-catch。 3. CPU密集型, `最佳线程数=CPU 核数 +1`。 4. IO密集型,`最佳线程数 =CPU 核数 * 2`。 5. IO密集型和CPU密集型交叉运行,`最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]`。 6. 线程池需要复用,不能盲目创建大量线程池。但是也需要根据业务不同使用不同的线程池,隔离影响。 7. 根据实际情况自定义拒绝策略。原创 2020-10-25 16:06:30 · 1271 阅读 · 0 评论 -
ThreadPoolExecutor源码解读(三)——如何优雅的关闭线程池(shutdown、shutdownNow、awaitTermination)
1.shutdown()会中断空闲工作线程,不会中断正在执行任务的工作线程,也不会清空工作队列,会等待所有已提交的任务执行完,但是拒绝新提交的任务。 2.shutdownNow(),会中断所有工作线程,并清空工作队列,拒绝新提交的任务。 3.关闭线程池,只调用`shutdown()`或者`shutdownNow()`是不够的,因为线程池并不一定立刻终止,还需要调用`awaitTermination`,循环检查`runState`是否到了最终状态`TERMINATED`。原创 2020-10-25 00:56:06 · 2781 阅读 · 0 评论 -
ThreadPoolExecutor源码解读(二)——execute提交任务,Worker详解。如何执行任务?如何回收空闲线程?
1. 可以调用`prestartAllCoreThreads`或者`prestartCoreThread`方法预先创建几个工作线程池,等待任务提交并执行。 2. 线程池中通过创建并启动`Worker`线程,执行用户提交的任务。 3. `Worker`继承于`AbstractQueuedSynchronizer`,是一把锁,实现了`Runnable`接口,又是一个线程。 4. 用户提交的`Runnable`任务`task`,启动`Worker`后自动执行`Worker`的`run()`,并直接调用task原创 2020-10-24 23:37:42 · 913 阅读 · 0 评论 -
ThreadPoolExecutor源码解读(一)——重新认识ThreadPoolExecutor(核心参数、生命周期、位运算、ThreadFactory、拒接策略)
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 线程数小于`corePoolSize`时,提交任务会继续创建线程,大于等于`corePoolSize`时,提交任务将加到工作队列中。 当工作队列已满,此时提交任务会创建线程,直到线程数达到`maximumPoolSize`,再提交任务触发拒绝策略。 - 默认情况下线程池中只会保留小于等于`corePoolSize`的线程池,原创 2020-10-23 01:12:02 · 573 阅读 · 0 评论 -
AQS源码解读(九)——Semaphore信号量原理详解
Semaphore翻译是信号量的意思,可以控制并发访问资源的数量。 Semaphore是在AQS基础上实现的共享锁,获取资源和释放资源都是调用的AQS中共享锁模板方法,故只需要看tryAcquireShared和tryReleaseShared在Semaphore中的实现。 获取资源许可是对`state`做减法,释放资源许可是对`state`做加法。原创 2020-10-08 00:43:11 · 555 阅读 · 0 评论 -
用一句话说明白CountDownLatch倒数器的原理
假设CountDownLatch从10开始倒数,就相当于一个大门有10把锁,只有10把锁都打开了,才能进门,否则都会被堵在门口。而调用countDown()就是在解锁,在没有解开所有锁之前调用await()就会阻塞。原创 2020-10-08 00:39:03 · 470 阅读 · 0 评论 -
AQS源码解读(七)——ReentrantReadWriteLock原理详解(读写锁是一把锁吗?如何一把锁两个状态?)
ReentrantReadWriteLock作为读写锁,整体架构和实现都与ReentrantLock类似,不同之处在于ReentrantReadWriteLock分为读锁和写锁,读锁是共享锁,可多个读线程共享一把锁;写锁是互斥锁,只能有一个线程持有锁。同样ReentrantReadWriteLock也是基于AQS实现的。原创 2020-10-07 21:15:53 · 870 阅读 · 3 评论 -
AQS源码解读(六)——从PROPAGATE和setHeadAndPropagate()分析共享锁的传播性
1. ReentrantReadWriteLock中PROPAGATE只是一个中间状态,共享锁的传播性由setHeadAndPropagate完成。 2. 对于有资源概念的Semaphore,PROPAGATE和setHeadAndPropagate组合完成共享锁的传播性。 3. 共享锁的传播性目的是尽快唤醒同步队列中等待的线程,使其尽快获取资源(锁),但是也有一定的副作用,可能会造成不必要的唤醒。原创 2020-09-17 14:41:44 · 3168 阅读 · 6 评论 -
AQS源码解读(五)——从acquireShared探索共享锁实现原理,何为共享?如何共享?
何为共享锁?共享锁就是多个线程可以共享一把锁,如ReentrantReadWriteLock的ReadLock是共享锁,Semaphore是共享锁,CountDownLatch是共享锁,且这三个都是基于AQS实现的。 在AQS中共享锁和独占锁一样,也实现了一套通用的模板,子类只需要实现如何获取锁(tryAcquireShared),如何释放锁(tryReleaseShared)的逻辑。原创 2020-10-07 13:56:10 · 1520 阅读 · 0 评论 -
AQS源码解读(四)——Condition原理详解(Object#wait/notify优化?singnal唤醒线程了吗?)
Condition的出现就是为了解决wait/notify不能针对性唤醒的问题。Condition也是需要和Lock配套使用,所以Condition也是Lock的一部分。Condition是如何解决wait/notify的问题的呢?原创 2020-10-07 01:08:19 · 633 阅读 · 0 评论 -
AQS源码解读(三)——ReentrantLock原理详解(Sync、NonfairSync、FairSync)
ASQ实现的是一套通用的模板,并不能完全直接应用于实际并发生产中,ReentrantLock就是根据AQS实现的互斥可重入锁。ReentrantLock和synchronized类似,互斥、阻塞、可重入,不同之处在于`synchronized`基于Java语法层面实现隐式加锁和释放锁,ReentrantLock基于API层面实现显式加锁和释放锁。 ReentrantLock实现了接口Lock,相较于synchronized,对锁的操作更灵活可控,同时根据不同的环境实现了公平锁和原创 2020-10-05 23:02:13 · 837 阅读 · 3 评论 -
AQS源码解读(二)——从acquireQueued探索独占锁实现原理,如何阻塞?如何唤醒?
1.AQS实现的锁终归还是个自旋锁(for (;;)),虽然不是无限自旋,也是给了一定的自旋次数,然后再阻塞。 2.AQS之所以是CLH的变种就在于判断阻塞时shouldParkAfterFailedAcquire,自旋检查前驱节点的状态。 3.取消获取锁的必须是双向队列,且只在可中断获取锁,中断导致获取失败后取消节点。取消过程中有可能唤醒取消节点的后继,但不一定是让其获取锁,而是让其链接一个正常的前驱。原创 2020-09-28 23:14:03 · 1261 阅读 · 14 评论 -
AQS源码解读(一)——AQS是什么?CLH变种体现在哪里?并发控制的核心在哪里?
AQS基于链表实现的双向同步队列,在CLH的基础上进行了变种。CLH是单向队列,其主要特点是自旋检查前驱节点的`locked`状态。而AQS同步队列是双向队列,每个节点也有状态`waitStatus`,而其并不是一直对前驱节点的状态自旋,而是自旋一段时间后阻塞让出cpu时间片(上下文切换),等待前驱节点主动唤醒后继节点,这就相当于CLH+MCS。原创 2020-09-26 21:40:50 · 1201 阅读 · 0 评论 -
AQS源码解读(番外篇)——四种自旋锁原理详解(Java代码实现SpinLock、TicketSpinLock、CLH、MCS)
自旋锁是为实现保护共享资源而提出的一种锁机制。自旋锁与Java中的synchronized和Lock不同,不会引起调用线程阻塞睡眠。如果有线程持有自旋锁,调用线程就会一直循环检测锁的状态,直到其他线程释放锁,调用线程才停止自旋,获取锁。原创 2020-09-23 12:59:09 · 960 阅读 · 1 评论 -
JUC源码解读文章目录JDK8(愿与你一起学习和探讨,排除万难,领略作者Doug Lea匠心思维)
JUC(java.util.concurrent)是Java源码中非常重要的一个版块,无论是CAS乐观锁还是Lock悲观锁,线程池、并发集合、阻塞队列等,在日常开发中都经常用到。 如果只是停留在简单使用层面,不去深究其原理,出现了BUG,也会茫然,手足无措;而阅读其源码,了解并研究其实现原理,JUC也就不会再像一个黑盒子,平时使用也会得心应手,同时还能学习到作者的编程思维。 学习JUC源码有这么多好处,百利而无一害,何不就此开始呢?我愿与你一起学习和探讨,排除万难,领略作者匠心思维。原创 2020-10-05 23:42:11 · 1045 阅读 · 3 评论