一、volatile
- volatile的功能:保证修饰变量的可见性和有序性
- 可见性:确保所有线程看到这个变量的值是一致的
- 如果确保可见性:
处理器在对volatile修饰的变量进行写 *** 作时,会进行lock add1汇编指令。Lock前缀的指令在多核处理下会引发两件事:
1、将当前处理器缓存行的数据写回到系统内存
2、这个写回内存的 *** 作会使其他cpu里缓存里该内存地址的数据无效 - 如何保证其他cpu里缓存该内存地址的数据无效:
实现缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己的缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址的数据被修改了,就会将当前处理器的缓存行数据置为无效状态,当前处理器再次对这个数据进行修改 *** 作的时候,会重写从系统内存中把数据读到处理缓存里。
二、Synchronized
-
synchronized实现原理:
synchronized实现同步的基础是:Java中每一种对象都可以作为锁。具体表现为以下3中形式:
1、对于普通同步方法,锁是当前实例对象
2、对于静态同步方法,锁是当前类的Class对象
3、对于同步方法块,锁是synchronized括号里配置的对象 -
锁信息存储位置和锁里包含的内容:
1、synchronized用的锁是存储在Java对象头里的,如果对象是数组类型,则用3个字宽存储对象头,如果对象是非数组类型,则用2个字宽存储。对象头包含3部分(数组类型对象):Mark Word:存储对象的hashcode和锁信息;Class metadata Address:存储对像类型数据的指针;Array length:数组的长度
2、锁里的内容包括:锁状态、(对象分代年龄、线程ID等)、锁标志位 -
锁升级:无锁->偏向锁->轻量级锁->重量级锁
偏向锁:
1、偏向锁的思想(乐观锁):锁不仅不存在竞争,而且总是由同一线程多次获得,等到竞争出现才释放锁
2、偏向锁的实现:当一个线程访问同步块区域并获取锁时,会在对象头的Mark Word和栈帧中的锁记录里存储偏向锁的线程ID,以后该线程在进入和退出同步块区域时不需要进行cas *** 作来加锁和释放锁,只会检查对象头里是否存储着指向当前线程的偏向锁。
3、偏向锁的撤销:当有其他线程竞争偏向锁时,持有偏向锁的线程才会释放锁,偏向锁的撤销需要等到全局安全点轻量级锁:
1、当偏向锁出现其他线程竞争锁时,会升级锁,尝试使用cas对像头的偏向锁执行当前线程。
2、轻量级的实现:线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用户存储锁记录的空间,并将对象头中的Mark Word赋值到锁记录中,然后线程尝试使用CAS将对象头中的Mark Word替换位执行锁记录地址的指针,如果成为获得锁,如果失败,当前线程尝试自旋来获得锁。
3、轻量级锁的解锁:使用cas方式将锁记录的Mark Word替换回对象头中,如果成功,没有发生锁竞争,如果失败,存在锁竞争,膨胀为重量级锁 -
锁的优缺点:
偏向锁:加锁和解锁不需要额外消耗,执行速度快;如果线程间存在竞争时,会有额外锁撤销的消耗,适应单线程访问同步块
轻量级锁:线程竞争不会阻塞,速度块;自旋消耗cpu;
重量级锁:不会消耗cpu;线程阻塞,响应时间慢
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)