并发编程面试文章地址链接
内容 | 博客链接 |
---|---|
并发编程面试题之常见面试题 | https://blog.csdn.net/weixin_38251871/article/details/104658674 |
并发编程面试题之 volatile 关键字 |
https://blog.csdn.net/weixin_38251871/article/details/104667384 |
并发编程面试题之 CAS |
https://blog.csdn.net/weixin_38251871/article/details/104667406 |
并发编程面试题之锁 | https://blog.csdn.net/weixin_38251871/article/details/104667392 |
并发编程面试题之阻塞队列 | 待完成… |
并发编程面试题之 AQS |
待完成… |
并发编程面试题之线程池 | 待完成… |
并发编程面试题之 synchronized 和 ReentrantLock 的区别 |
https://blog.csdn.net/weixin_38251871/article/details/104667532 |
并发编程面试题之 CyclicBarrier、CountDownLatch、Semaphore |
待完成… |
并发编程面试题之 ConcurrentHashMap |
https://blog.csdn.net/weixin_38251871/article/details/104667433 |
并发编程面试题之 synchronized 实现原理 |
https://blog.csdn.net/weixin_38251871/article/details/104667415 |
synchronized 核心组件
Wait Set:
放置调用Object#wait()
方法后被阻塞的线程Contention List:
竞争队列,所有请求锁的线程先放置在这个竞争队列中Entry List:
竞争队列中有资格成为候选资源的线程被移动到Entry List
中OnDeck:
无论什么时候, 最多只有一个线程正在竞争锁资源,该线程就叫OnDeck
Owner:
当前已经获取到锁资源的线程叫做Owner
!Owner:
当前释放锁的线程
synchronized 底层实现原理
JVM
每次从队列的尾部取出一个数据用于锁竞争候选者(OnDeck)
,但是在并发的情况下,Contention List
会被大量的并发线程进行CAS
访问,为了降低对尾部元素的竞争,JVM
会将一部分线程移动到Entry List
中作为候选竞争线程Owner
线程会在unlock
时将Contention List
中的部分线程迁移到Entry List
中,并指定Entry List
中的某个线程为OnDeck
线程Owner·
线程并不直接把锁传递给OnDeck
线程, 而是把锁竞争的权利交给OnDeck
,OnDeck
需要重新竞争锁。这样虽然牺牲了一些公平性, 但是能极大的提升系统的吞吐量, 在JVM
中, 这种行为称之为竞争切换
OnDeck
线程获取到锁资源后会变成Owner
线程, 而没有获取到锁的线程仍然停留在Entry List
中, 如果Owner
线程被Object#wait()
方法阻塞,则转移到Wait Set
队列中, 直到某个时刻通过Object#notify()/Object#notifyAll()
方法唤醒, 会重新进入到Entry List
中- 处于
Contention List、Entry List、Wait Set
中的线程都处于阻塞状态, 该阻塞是由操作系统来完成的 synchronized
是非公平锁,synchronized
在线程进入Contention List
的时候, 等待的线程会先尝试自旋获取锁, 如果获取不到锁就进入Contention List
中, 这明显对于已经进入到队列的线程是不公平的, 还有一个不公平的事情就是自旋获取锁的线程还可能直接抢占OnDeck
线程的锁资源- 每个对象都有一个
monitor
对象, 加锁就是在竞争monitor
对象, 代码块加锁就是在前后分别加上monitorenter 与 monitorexit
指令来实现的, 方法加锁就是通过一个标记位来判断的 synchronized
是一个重量级的操作, 需要调用操作系统相关接口, 性能低效. 有可能给线程加锁的时间比操作程序的时间更多- 值得庆幸的是, 在
JDK 1.6
之后,synchronized
进行了很多的优化,适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等
, 在效率上有本质上的提高, 在JDK 1.7/JDK 1.8
中都对synchronized
关键字的实现机制做了优化, 都是在对象头中由标记位, 不需要经过操作系统加锁 - 锁升级可以从
偏向锁 --> 轻量级锁 --> 重量级锁
, 这种升级过程叫锁膨胀 JDK 1.6
默认开启偏向锁和轻量级锁, 可以通过-XX:-UseBiasedLocking
来禁用偏向锁