并发编程面试题之synchronized实现原理


并发编程面试文章地址链接

内容 博客链接
并发编程面试题之常见面试题 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 待完成…
并发编程面试题之线程池 待完成…
并发编程面试题之 synchronizedReentrantLock 的区别 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 来禁用偏向锁
发布了17 篇原创文章 · 获赞 3 · 访问量 539

猜你喜欢

转载自blog.csdn.net/weixin_38251871/article/details/104667415