并发编程面试文章地址链接
| 内容 | 博客链接 |
|---|---|
| 并发编程面试题之常见面试题 | 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
|
待完成… |
| 并发编程面试题之线程池 | https://blog.csdn.net/weixin_38251871/article/details/104675416 |
并发编程面试题之 synchronized 和 ReentrantLock 的区别 |
https://blog.csdn.net/weixin_38251871/article/details/104667532 |
并发编程面试题之 CyclicBarrier、CountDownLatch、Semaphore
|
https://blog.csdn.net/weixin_38251871/article/details/104677582 |
并发编程面试题之 ConcurrentHashMap
|
https://blog.csdn.net/weixin_38251871/article/details/104667433 |
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来禁用偏向锁