Sychronized的锁升级过程是怎样的

原创
05/11 17:24
阅读数 41

`synchronized`关键字的锁升级过程是Java为了提高锁的性能,减少在无竞争或多线程轻度竞争情况下的开销而设计的一套机制。这一过程主要涉及以下四个阶段:

1. 无锁状态:当一个对象刚创建时,并没有锁与其关联,处于无锁状态。

2. 偏向锁(Biased Locking):
-初始化:当第一个线程访问同步代码块或方法时,JVM会将对象头的MarkWord设置为偏向锁,并记录这个线程的ID。
-偏向:如果后续的访问仍然是由这个线程发起的,无需进行同步操作,直接执行代码即可,因为锁已经偏向于这个线程
-撤销:如果有其他线程尝试访问这个同步块,偏向锁将被撤销,并进入轻量级锁状态。撤销时会有一定的开销,包括检查偏向锁标识、CAS操作尝试清除偏向锁等。

3.轻量级锁(Lightweight Locking):
-自旋:当有第二个线程尝试获取锁时,偏向锁被撤销,转换为轻量级锁。线程会在自己的栈帧中创建一个称为Lock Record的空间,用于存储锁的Mark Word的拷贝。
-CAS操作:然后通过CAS操作尝试将对象头的Mark Word替换为指向Lock Record的指针。如果成功,线程获得锁;失败,则说明存在竞争,线程将自旋一段时间,不断尝试CAS操作直到成功或达到自旋上限。
-自旋失败:如果自旋超过一定次数(自旋阈值)仍未获得锁,轻量级锁将升级为重量级锁。

4.重量级锁(Heavyweight Locking):
-监视器锁:当多个线程竞争激烈,轻量级锁无法有效处理时,锁升级为重量级锁。此时,JVM会调用操作系统的互斥量(mutex)来实现线程阻塞和唤醒,这会导致线程挂起和恢复,开销较大。
-阻塞与唤醒:未获取到锁的线程会被阻塞,进入等待队列,而持有锁的线程执行完毕后,会唤醒队列中的下一个等待线程。

public class Synchronized2Example {
    // 对象锁示例
    public synchronized void method1() {
        System.out.println("Method 1 executing by " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000); // 模拟耗时操作,增加锁竞争的可能性
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 代码块锁示例,锁定的是object实例
    private Object object = new Object();

    public void method2() {
        synchronized (object) { // 这里使用的是对象锁
            System.out.println("Method 2 executing by " + Thread.currentThread().getName());
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Synchronized2Example example = new Synchronized2Example();

        Thread t1 = new Thread(() -> example.method1(), "Thread-1");
        Thread t2 = new Thread(() -> example.method2(), "Thread-2");

        t1.start();
        t2.start();

    }
}

运行这段程序,可以看到两个线程分别尝试访问这两个同步方法或代码块。虽然这个示例并不能直观展示锁升级的过程,但它可以帮助理解synchronized如何保证线程间的同步。

 

锁升级的整个过程是不可逆的,一旦升级到重量级锁,就不会再降级回轻量级锁或偏向锁。这种设计旨在根据竞争程度动态调整锁策略,以达到最佳性能平衡。

 

注:

下面是对各个锁的解释

1.偏向锁:在锁对象的对象头中记录一下当前获取到该锁的线程ID,该线程下次如果又来获取
该锁就可以直接获取到了,也就是支持锁重入


2.轻量级锁:由偏向锁升级而来,当一个线程获取到锁后,此时这把锁是偏向锁,此时如果有
第二个线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开来,轻量级锁底层是通过自旋来实现的,并不会阻塞线程


3.如果自旋次数过多仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞


4.自旋锁∶自旋锁就是线程在获取锁的过程中,不会去阻塞线程,也就无所谓唤醒线程,阻塞和唤醒这两个步骤都是需要操作系统去进行的,比较消耗时间,自旋锁是线程通过CAS获取预期的一个标记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程一直在运行中,相对而言没有使用太多的操作系统资源,比较轻量。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部