一、Java 对象结构
Java 对象在 JVM 中的结构主要包括三部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
其中,对象头是一个非常关键的部分,因为它包含了对象运行时的关键信息,如锁状态、哈希码等。
对象头(Header) 主要包含两部分:
-
Mark Word(标记字):用于存储对象自身的运行时数据,如 GC 标志位、哈希码、锁状态等信息。这部分信息在 JVM 中起着至关重要的作用,尤其是在多线程同步时。Mark Word 的大小和具体结构会根据 JVM 的实现和版本有所不同,但通常包括锁状态、偏向锁标识、指向锁记录的指针等。
-
Class Pointer(类对象指针):用于存放方法区 Class 对象的地址,通过这个指针,虚拟机可以确定这个对象是哪个类的实例。
如果对象是一个 Java 数组,则还会包含一个Array Length(数组长度)字段,用于记录数组的长度。
二、对象锁的升级
在 Java 中,synchronized 关键字用于控制多线程同步,保证访问共享资源时的线程安全。
在 JDK 1.6 之前,synchronized 实现的是重量级锁,效率较低。
从 JDK 1.6 开始,JVM 对 synchronized 进行了优化,引入了偏向锁和轻量级锁,以提高锁的获取与释放效率。
Java 内置锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁和重量级锁。这些状态会随着线程的竞争情况逐渐升级,且是不可逆的过程,即锁可以升级但不能降级。
锁升级过程:
-
无锁状态:对象刚被创建时,没有任何线程来竞争,处于无锁状态。此时,偏向锁标识位是 0,锁状态为 01。
-
偏向锁状态:当某个线程第一次访问某个同步代码块时,JVM 会将其设置为偏向锁状态,表示这个锁偏爱该线程。此后,只要该线程再次访问这个同步代码块,就可以直接获得锁,而无需进行任何检查和切换。偏向锁在竞争不激烈的情况下效率非常高。
-
轻量级锁状态:当有其他线程尝试访问同一个同步代码块时,偏向锁状态会被撤销,并升级为轻量级锁状态。轻量级锁通过自旋锁的方式来实现,即线程会循环等待锁的释放,而不会立即阻塞。如果持有锁的线程能在很短时间内释放锁,那么等待的线程就可以立即获得锁,从而避免了线程阻塞和唤醒的开销。
-
重量级锁状态:如果轻量级锁自旋尝试获取锁失败,或者持有锁的线程执行时间较长,超过了自旋等待的最大时间,那么轻量级锁会膨胀为重量级锁。此时,线程会被阻塞,并等待持有锁的线程释放锁。重量级锁通过对象监视器(Monitor)来实现,线程会进入阻塞队列,等待锁的释放。
package com.hmblogs.backend.study.thread;
public class UpgradeThread {
// 示例代码,展示如何在多线程环境中安全地升级对象结构
// 假设的对象结构
static class MyObject {
int version;
Object data;
MyObject(int version, Object data) {
this.version = version;
this.data = data;
}
void upgrade(MyObject newVersion) {
// 这里可以加入升级前的检查,如版本比较等
synchronized (this) {
this.version = newVersion.version;
this.data = newVersion.data; // 执行实际的升级操作
}
}
}
// 升级线程
static class UpgradeThread2 extends Thread {
MyObject currentObject;
MyObject newVersion;
UpgradeThread2(MyObject currentObject, MyObject newVersion) {
this.currentObject = currentObject;
this.newVersion = newVersion;
}
@Override
public void run() {
currentObject.upgrade(newVersion);
}
}
public static void main(String[] args) {
MyObject currentObject = new MyObject(1, "version 1 data");
MyObject newVersion = new MyObject(2, "version 2 data");
Thread upgradeThread = new UpgradeThread2(currentObject, newVersion);
upgradeThread.start();
// 确保升级线程执行完成
try {
upgradeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印升级后的对象数据
System.out.println("Current version: " + currentObject.version);
System.out.println("Current data: " + currentObject.data);
}
}
这个代码示例展示了如何在 Java 中创建一个简单的对象结构,并在多线程环境中安全地升级它。
MyObject 类包含了一个 upgrade 方法,该方法通过同步块来保证在多线程条件下的安全升级操作。
UpgradeThread 类继承自 Thread,用于执行升级动作。
在 main 方法中,我们创建了两个版本的对象实例,并启动了一个新的线程来执行升级操作。
最后,我们等待线程执行完成并打印升级后的对象数据。
总的来说,Java 对象锁的升级是为了提高多线程同步的效率,减少锁的获取和释放的开销。
通过引入偏向锁和轻量级锁,JVM 能够在多线程竞争不激烈的情况下,以较低的开销实现同步,从而提高程序的性能。