源码
源码地址:jdk8u
源码地址:OpenJDK / jdk8u / jdk8u / hotspot directory /src/share/vm/runtime/
源码地址:OpenJDK / jdk8u / jdk8u / jdk directory /src/share/native/java/lang/
CAS
compare and swap
compare and exchange
ABA问题
解决
在汇编层级上的lock + compare and exchange
AtomicXXX
如果是用AtomicXXX就可以不用上锁了,因为已经 lock comxchg 了
源码
unsafe.cpp->Atomic::cmpxchg->atomic.cpp->atomic_linux_x86.inline.hpp->Atomic::cmpxchg->_asm_ volatile(LOCK_IF_MP(%4)"comxchgl..."...);
LOCK_IF_MP保证了其原子性
对象在内存中的存储布局
markword对象头:
包括两部分信息
第一部分:对象自身的运行时数据,如哈希码,GC分代年龄, 锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等,这部分数据的长度在32 位和64位的虚拟机中分别为32 bit和64 bit,官方称它为“Mark Word”。
第二部分:类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确 定这个对象是哪个类的实例。如果对象是一个java数组,那在对象头中还必须有一 块用于记录数组长度的数据。
instance data实例数据:
是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。
padding对齐填充:
对齐填充不是必然存在的。HotSpot VM的自动内存管理系统要求对象起始地址必须 是8字节的整数倍,也就是说对象的大小必须是8字节的整数倍。而对象头部分正好 是8字节的整数倍。因此,当对象实例数据部分没有对齐时,就需要通过对其补充 来补全了。
普通对象
markword-class pointer-instance data-padding
数组
markword-class pointer-length-instance data-padding
分析工具
JOL
ClassLayout.parseInstance(object).toPrintable();
给对象上锁
也就是在markword的value上进行改变
升级过程
偏向锁
偏向于第一次用到这个锁的线程,把自己线程的id放到mardword
java -XX:+PrintFlagsFinal -version
java -XX:+PrintFlagsFinal -version | grep BiasedLocking
BiasedLockingStartupDelay 偏向锁会延迟一定的时间才打开,会等JVM启动完以后再启用
轻量级锁(自旋锁)
用CAS的方式修改markword,把自己的id放进去,谁抢成功了这把锁就是谁的。
Lock Record 的指针
重量级锁
C++的层面ObjectMonitor
源码地址:
synchronized的缺点:
-
1.当一个代码块被synchronized修饰的时候,一个线程获取到了锁,并且执行代码块,那么其他的线程需要等待正在使用的线程释放掉这个锁,那么释放锁的方法只有两种,一种是代码执行完毕自动释放,一种是发生异常以后jvm会让线程去释放锁。那么如果这个正在执行的线程遇到什么问题,比如等待I0或者调用sleep方法等等被阻塞了,无法释放锁,而这时候其他线程只能一直等待,将会特别影响效率。那么有没有一种办法让其他线程不必一直傻乎乎的等在这里吗?
-
2.当一个文件,同时被多个线程操作时,读操作和写操作会发生冲突,写操作和写操作会发生冲突,而读操作和读操作并不会冲突,但是如果我们用synchronized的话,会导致一个线程在读的时候,其他线程想要读的话只能等待,那么有什么办法能不锁读操作吗?
-
3.在使用synchronized时,我们无法得知线程是否成功获取到锁,那么有什么办法能知道是否获取到锁吗?
应用场景
- 在资源竞争不是很激烈的情况下,synchronized的性能要优化于ReentrantLock。
- 但是在资源竞争很激烈的情况下,synchronized的性能会下降几十倍,但是ReentrantLock的性能能维持常态。
- ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)
| 锁状态 | 25位 | 31位 | 1位 | 4位 | 1位(偏向锁位) | 2位(锁标志位) | |
|---|---|---|---|---|---|---|---|
| 无锁态 | unused | hashCode(如果有调用) | unused | 分代年龄 | 0 | 0 | 1 |
| 锁状态 | 54位 | 2位 | 1位 | 4位 | 1位(偏向锁位) | 2位(锁标志位) | |
|---|---|---|---|---|---|---|---|
| 偏向锁 | 当前线程指针JavaThread* | Epoch | unused | 分代年龄 | 1 | 0 | 1 |
| 锁状态 | 62位 | 2位(锁标志位) | |
|---|---|---|---|
| 轻量级锁(自旋锁) | 指向线程中Lock Record的指针 | 0 | 1 |
| 重量级锁 | 指向互斥量(重量级锁)的指针 | 1 | 0 |
| GC标记信息 | CMS过程用到的标记信息 | 1 | 1 |