在JVM中我们的Java对象是有基本的存储结构的大致结构如下
+-------------------+-----------------+-----------------+
| Mark Word | Class Pointer | Instance Data |
+-------------------+-----------------+-----------------+
| (Object Header) | (Object Header) | (Object Fields) |
+-------------------+-----------------+-----------------+
| Padding | | |
+-------------------+-----------------+-----------------+
对象结构介绍(想详细了解的小伙伴可以googleJava对象基本存储结构)
- Mark Word:
-
- 在未加锁时,Mark Word 存储对象的哈希码、GC 状态等。
- 在加锁时,Mark Word 可能会存储锁的相关信息(如锁的持有者、锁的计数等)。
- Class Pointer:
-
- 指向对象的类元数据,用于获取对象的类型信息和方法等。
- Instance Data:
-
- 包含对象的实例字段值,如对象的属性数据。
- Padding:
-
- 据 JVM 实现和硬件架构的要求添加的填充字节,以保证对象在内存中的对齐。
Synchronized 关键字就是借助MarkWord 的数据空间存放了 锁是否被持有的标识
Synchronized 在加锁时有四种状态
1. 无锁状态(No Lock)
在无锁状态下,对象的 Mark Word 中包含对象的标记信息,如哈希码、GC 信息等。此时没有线程持有该对象的锁。
2. 偏向锁(Biased Locking)
偏向锁是一种优化机制,用于减少无竞争情况下的同步开销。当一个线程第一次获取对象的锁时,JVM 会将该线程的 ID 存储在对象的 Mark Word 中,并将锁状态标记为偏向锁状态。
偏向锁获取过程:
- 当线程 T1 第一次获取锁时,Mark Word 中的偏向锁标志和线程 ID 会被设置为 T1。
- 后续 T1 再次获取锁时,不需要进行任何同步操作,因为 Mark Word 中已经包含了 T1 的 ID。
偏向锁撤销过程:
- 当另一个线程(如 T2)尝试获取该锁时,发现锁已经被偏向 T1,则需要撤销偏向锁。此时,JVM 会暂停拥有偏向锁的线程 T1,检查 T1 的状态,并尝试将锁状态升级为轻量级锁或重量级锁。
3. 轻量级锁(Lightweight Locking)
轻量级锁通过使用 CAS 操作和锁记录(Lock Record)来减少无竞争情况下的锁开销。当偏向锁撤销后,或者多个线程尝试同时获取同一个对象的锁时,JVM 会尝试将锁状态升级为轻量级锁。
轻量级锁获取过程:
- 线程 T2 创建一个锁记录(Lock Record)并将对象的 Mark Word 复制到锁记录中。
- T2 使用 CAS 操作尝试将对象的 Mark Word 替换为指向锁记录的指针。如果 CAS 操作成功,T2 获得轻量级锁。
- 如果 CAS 操作失败,表示其他线程已经获取了该锁,此时锁会升级为重量级锁。
4. 重量级锁(Heavyweight Locking)
重量级锁使用操作系统的互斥量(Mutex)来实现线程同步。当轻量级锁竞争失败时,JVM 会将锁升级为重量级锁,所有试图获取该锁的线程都会被阻塞,直到持有锁的线程释放锁。
重量级锁获取过程:
- 当轻量级锁竞争失败时,JVM 会将对象的 Mark Word 设置为指向重量级锁的指针,锁状态标记为重量级锁。
- 试图获取该锁的线程会被阻塞,并进入等待队列,直到持有锁的线程释放锁。
锁的升级流程
+------------+
| 无锁状态 |
+------------+
|
v
+------------+
| 偏向锁状态 |
+------------+
|
v
+------------+
| 轻量级锁状态 |
+------------+
|
v
+------------+
| 重量级锁状态 |
+------------+
因为锁的升级过程时 JVM进行操作的。所以我通过以下的代码模拟了整个过程
package org.example.MultiThreadShare;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class SimulatedLock {
// 锁状态
private static final int UNLOCKED = 0;// 无锁
private static final int BIASED = 1;// 偏向锁
private static final int LIGHTWEIGHT = 2;//轻量级锁
private static final int HEAVYWEIGHT = 3;// 重量级锁
// Lock state
private final AtomicInteger state = new AtomicInteger(UNLOCKED);
// Owner of the lock
private final AtomicReference<Thread> owner = new AtomicReference<>();
// For lightweight lock
private final AtomicReference<Thread> lightweightLockOwner = new AtomicReference<>();
private Node waitNode = new Node();
public class Node {
private Thread current;
private Node next;
private AtomicInteger state = new AtomicInteger(0);
public Node(Thread current, Node next) {
this.current = current;
this.next = next;
}
public Node(Thread current) {
this.current = current;
}
public Node() {
}
public void setWait(Thread current) {
while (state.compareAndSet(0, 1)) {
Node currentNode = this;
while (currentNode.next != null) {
currentNode = currentNode.next;
}
currentNode.next = new Node(current);
}
state.set(0);
}
}
public void lock() {
Thread currentThread = Thread.currentThread();
// 模拟锁的升级过程
while (true) {
int currentState = state.get();
if (currentState == UNLOCKED) {
// 尝试通过偏向锁的方式获取锁
if (state.compareAndSet(UNLOCKED, BIASED)) {
owner.set(currentThread);
return;
}
} else if (currentState == BIASED) {
// 如果锁是偏向锁,并且当前线程不是锁持有者,则尝试将偏向锁升级成轻量级锁
if (owner.get() != currentThread) {
if (state.compareAndSet(BIASED, LIGHTWEIGHT)) {
lightweightLockOwner.set(currentThread);
return;
}
}
} else if (currentState == LIGHTWEIGHT) {
// 如果锁是轻量级锁,并且当前线程不是锁的持有者,则尝试升级成重量级锁
if (lightweightLockOwner.get() != currentThread) {
if (state.compareAndSet(LIGHTWEIGHT, HEAVYWEIGHT)) {
return;
}
}
} else if (currentState == HEAVYWEIGHT) {
// 重量级锁(此状态等待)
if (owner.get() != currentThread) {
while (state.get() == HEAVYWEIGHT) {
// Busy-wait (simulating waiting on a heavyweight lock)
waitNode.setWait(currentThread);
LockSupport.park();
}
return;
}
}
}
}
public void unlock() {
Thread currentThread = Thread.currentThread();
if (state.get() == HEAVYWEIGHT && owner.get() == currentThread) {
state.set(UNLOCKED);
owner.set(null);
} else if (state.get() == LIGHTWEIGHT && lightweightLockOwner.get() == currentThread) {
state.set(UNLOCKED);
lightweightLockOwner.set(null);
} else if (state.get() == BIASED && owner.get() == currentThread) {
state.set(UNLOCKED);
owner.set(null);
}
Node currentNode = waitNode;
while (currentNode.next != null) {
currentNode = currentNode.next;
}
currentNode.next=null;
Thread thread = currentNode.current;
if (null != thread)
LockSupport.unpark(thread);
}
public static void main(String[] args) throws InterruptedException {
SimulatedLock lock = new SimulatedLock();
Runnable task = () -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " is working");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
};
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
t1.start();
t2.start();
t1.join();
t2.join();
}
}
关键字使用姿势及常见问题
-
在使用关键字加锁的时候 如果发生异常。那锁和异常栈的先后顺序是什么
- 先释放当前锁,然后打印异常栈 详细见下面的代码
public class SynchronizedExceptionExample {
private final Object lock = new Object();
public static void main(String[] args) {
SynchronizedExceptionExample example = new SynchronizedExceptionExample();
Thread thread1 = new Thread(() -> example.synchronizedMethod("Thread 1"));
Thread thread2 = new Thread(() -> example.synchronizedMethod("Thread 2"));
thread1.start();
thread2.start();
}
public void synchronizedMethod(String threadName) {
synchronized (lock) {
System.out.println(threadName + " has acquired the lock.");
try {
if ("Thread 1".equals(threadName)) {
throw new RuntimeException("Exception in " + threadName);
}
Thread.sleep(1000); // 模拟长时间运行的任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println(threadName + " is releasing the lock.");
}
}
}
}