详解Java对象结构与对象锁的升级
Java对象结构
Java对象在内存中的实际存储由三部分组成:对象头、实例数据和对齐填充。
对象头
对象头是Java对象的一部分,用于存储对象自己的运行时数据,包括以下内容:
- Mark Word: 用来锁定对象、记录对象哈希值、记录对象所属的分代年龄等信息。
- Class: 指向对象的Class对象。
在Java 8中,对象头的大小为12字节。
实例数据
实例数据存储对象的实例变量,实例数据相对于对象头的大小是不定的,因为它取决于对象的实例变量数量和类型。
对齐填充
为了对齐对象在内存中的地址,可能需要在实例数据末尾补充填充数据,使得对象的大小是8字节的整数倍。在Java 8中,所需的填充量最多为7个字节。
对象锁的升级
Java中的对象锁分为偏向锁、轻量级锁和重量级锁。
偏向锁
偏向锁是一种针对加锁频繁性不高的场景的优化,偏向锁的意思是,偏向某个线程,这个线程在获取锁之后,当再次进入同步块时,因为上一次已经获取过锁了,所以可以直接获得锁,而不需要重新去竞争。
下面是一个偏向锁示例:
public class BiasedLockDemo {
private static final int COUNT = 10_000_000;
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
synchronized (obj) {
// do nothing
}
}
System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");
Thread.sleep(10000);
start = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
synchronized (obj) {
// do nothing
}
}
System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");
}
}
在上面的示例中,第一次进入同步块时,JVM会偏向此线程,并记录对象的Mark Word为偏向锁模式。因为只有一个线程,所以不会有竞争,性能最优。第二次进入同步块时,因为对象已经记录了偏向线程,所以直接判断Mark Word是否指向该线程,如果是,则直接获取锁,不再竞争锁。
轻量级锁
如果一个线程没有成功获取偏向锁,或者偏向锁被取消了,那么它就会尝试轻量级锁来获取锁。轻量级锁使用CAS操作,将对象头中的Mark Word复制到线程的栈帧中,表示当前线程已经获得了这个锁。如果CAS操作成功,则当前线程成功获得锁,继续执行同步块。如果CAS操作失败,表示有竞争,此时当前线程会自旋一段时间,等待其他线程释放锁。
下面是一个轻量级锁示例:
public class LightweightLockDemo {
private static final int COUNT = 10_000_000;
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
synchronized (obj) {
// do nothing
}
}
}, "thread-1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
synchronized (obj) {
// do nothing
}
}
}, "thread-2");
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");
}
}
在上面的示例中,两个线程同时竞争同一个对象锁,因此会导致竞争,此时第二个线程会自旋等待第一个线程释放锁。
重量级锁
如果轻量级锁获取失败,则会进入重量级锁模式。此时线程会进入操作系统的内核态,进入阻塞状态,等待其他线程释放锁。
下面是一个重量级锁示例:
public class HeavyweightLockDemo {
private static final int COUNT = 10_000_000;
public static void main(String[] args) {
Object obj = new Object();
synchronized (obj) {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在上面的示例中,线程进入同步块后,会一直等待100秒,此时其他线程无法获取锁,只能等待该线程释放锁,因此此时已经进入重量级锁模式。
总结
Java对象在内存中的实际存储由三部分组成:对象头、实例数据和对齐填充。对象锁分为偏向锁、轻量级锁和重量级锁。在多线程并发环境下,不同的锁模式可以优化性能,提升程序的吞吐量。
以上就是Java对象结构与对象锁升级的详细攻略,希望能够对你有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java对象结构与对象锁的升级 - Python技术站