synchronized的实现原理与应用

1、synchronized

1.1、synchronized的实现

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
 
1.2、synchronized实现同步的基
 
1、对 于普通同步方法, 是当前 象。
2、对 于静 同步方法, 是当前 Class 象。
3、对 于同步方法 Synchonized 括号里配置的 象。

1.3、synchronized的底层是怎么实现的?

 三个特性:原子性、可见性、有序性。

 1)、 同步代码块底层是通过一个monitor的对象来完成monitor是由ObjectMonitor实现的, ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成  ObjectWaiter对象   ),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合,当线程获取到对象的      monitor  后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释     放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSe t集合中等待被唤醒。若当前线程执行完毕也将释放   monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁MonitorEnter指令:插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的 锁;MonitorExit指令:插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit;
 
2)、 同步方法:方法级的同步是隐式,即无需通过字节码指令来控制的,通过 ACC_synchronized 方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用   的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间,执行线程持有了    monitor,其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛 出了异常,并且在方法内部无法处理此异常,那这个同   步方法所持有的monitor将在异常抛到同步方法之外时自动释放
 

2、Java对象头

synchronized 用的 是存在 Java 里的。如果 象是数 组类 型, 机用 3 个字 宽 (Word )存 储对 ,如果 象是非数 组类 型, 2 储对 。在 32 位虚 机中, 1 宽 等于4 ,即 32bit ,如表
 
 
长度内容说明
32/64bitMark Word存储对象的hashcode或锁信息等
32/64bitClass Metadata Address存储到对象类型的数据指针
32/32bitArray Length数组的长度(如果当前对象为数组)

1、Java里的Mark Word里默储对象的HashCode、分代年锁标记位。

2、在运行期Mark Word里存的数据会随着锁标志位的化而

3、在64位虚机下,Mark Word64bit大小的,

3、锁的升级与对比

为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。
 
1.偏向锁
 
大多数情况下, 不存在多 线 竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。
 

1、偏向锁的撤销

偏向 使用了一种等到 争出 的机制,所以当其他 线 尝试竞 争偏向 锁时 , 持有偏向锁 线 程才会 。偏向 的撤 ,需要等待全局安全点(在 时间 点上没有正 在执 行的字 节码 )。它会首先 有偏向 线 程,然后 检查 持有偏向 线 程是否活着, 如果线 程不 于活 头设 置成无 ;如果 线 程仍然活着, 有偏向 栈会被执 行,遍 偏向 象的 锁记录 中的 锁记录 Mark Word 要么重新偏向于其他线程,要么恢复到无 或者 标记对 象不适合作 偏向 ,最后 停的 线 程。
 
2、 偏向
 
偏向 Java 7 里是默 启用的,但是它在 用程序启 几秒 之后才激活,如 有必要可以使用JVM参数来关 -XX:BiasedLockingStartupDelay=0 。如果你确定 用程 序里所有的锁 通常情况下 争状 ,可以通 JVM 参数关 偏向 -XX:- UseBiasedLocking=false,那么程序默 级锁 态。
 
2.轻量级锁
 
1 级锁
线 程在 行同步 之前, JVM 会先在当前 线 程的 栈桢 建用于存 储锁记录 的空 ,并 将对 中的 Mark Word 复制到 锁记录 中,官方称 Displaced Mark Word 。然后 线 尝试 使用 CAS将 中的 Mark Word 换为 指向 锁记录 的指 。如果成功,当前 线 ,如果失败,表示其他 线 ,当前 线 程便 尝试 使用自旋来
2 级锁
锁时 ,会使用原子的 CAS 操作将 Displaced Mark Word 回到 ,如果成功,则 表示没有 生。如果失 ,表示当前 存在 争, 就会膨 成重量 级锁 2-2 是 两个线 程同 夺锁 的流程
 

3.锁的优缺点对比

 

优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗 如果线程存在竞争会带来额外锁撤销的消耗只有一个线程访问同步块的场景
轻量级锁竞争的线程不会阻塞提高的程序的响应速度 如果始终得不到锁竞争线程会使用自旋消耗CPU

追求响应时间

同步块执行非常快

重量级锁线程竞争不使用自旋,不消耗CPU 线程阻塞响应时间慢

追求吞吐量

执行时间较长

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值