Synchronized锁的升级流程详解

在Java多线程编程中,synchronized关键字用于确保在同一时刻只有一个线程可以访问被锁定的资源,从而维护数据的一致性和安全性。然而,在多线程环境中,锁的频繁获取和释放会带来性能开销。为了提高性能,Java虚拟机(JVM)在JDK 1.6及以后的版本中引入了锁的升级机制,通过动态调整锁的策略来减少同步操作的开销。本文将详细解释synchronized锁的升级流程,包括无锁状态、偏向锁、轻量级锁和重量级锁四种状态及其转换过程。

锁的状态

无锁状态

当一个对象刚被创建时,它处于无锁状态,此时没有线程持有锁,所有访问同步代码块的线程都是无锁竞争状态。在这种状态下,对象的Mark Word(对象头的一部分,用于存储锁状态及线程信息)没有记录任何锁信息。

偏向锁

偏向锁是为了解决单线程访问共享资源的场景而设计的。当一个线程首次获得对象锁时,JVM会将锁设置为偏向锁,并将锁对象的Mark Word中的线程ID设置为当前线程的ID。后续当这个线程再次请求相同的锁时,只需检查Mark Word中的线程ID是否与当前线程ID一致。如果一致,说明还是原来的线程持有锁,可以直接进入同步代码块,无需进行额外的同步操作。偏向锁减少了轻量级锁中CAS操作的开销,提高了性能。

轻量级锁

当有第二个线程尝试获取已被偏向锁锁定的对象时,偏向锁失效,JVM会尝试升级为轻量级锁。线程会在当前线程栈中创建一个锁记录(Lock Record),并将锁对象的Mark Word替换为指向锁记录的指针,同时在锁记录中存储当前线程的ID和一个指向原Mark Word副本的指针。使用CAS操作尝试将锁对象的Mark Word设置为指向锁记录的指针。如果成功,线程获得轻量级锁并执行同步代码;如果失败(即有其他线程已持有锁),则进入下一步骤。轻量级锁适用于短暂的、低竞争的同步场景,通过自旋等待和CAS操作避免了线程切换的开销。

重量级锁

当自旋尝试失败或自旋超过一定阈值,或者系统检测到多个线程长期竞争同一锁时,轻量级锁会升级为重量级锁。重量级锁通常涉及操作系统级别的互斥量(Mutex),线程在无法获得锁时会被挂起,不再消耗CPU资源,直到持有锁的线程释放锁后,操作系统再唤醒等待队列中的下一个线程。重量级锁提供了严格的互斥保证,适用于高竞争或锁占用时间较长的场景,虽然开销较大,但能有效防止过多线程同时阻塞在自旋状态。

Mark Word

Mark Word是对象头中最重要的部分,它是一个特殊的字段,用于存储对象的元数据信息,包括锁状态和线程信息。在64位JVM中,Mark Word占用64位。当一个共享资源首次被某个线程访问时,锁就会从无锁状态升级到偏向锁状态,偏向锁会在Mark Word的偏向线程ID里存储当前线程的操作系统线程ID,偏向锁标识位是1,锁标识位是01。此后如果当前线程再次进入临界区域时,只比较这个偏向线程ID即可。

锁升级流程

无锁状态到偏向锁

当一个对象首次被某个线程访问时,它处于无锁状态。当第一个线程访问同步代码块或方法时,JVM会将对象头的Mark Word设置为偏向锁,并记录这个线程的ID。此时,如果后续的访问仍然是由这个线程发起的,无需进行同步操作,直接执行代码即可,因为锁已经偏向于这个线程。

偏向锁到轻量级锁

如果有其他线程尝试访问这个同步块,偏向锁将被撤销,并进入轻量级锁状态。撤销时会有一定的开销,包括检查偏向锁标识、CAS操作尝试清除偏向锁等。当有第二个线程尝试获取锁时,偏向锁被撤销,转换为轻量级锁。线程会在自己的栈帧中创建一个称为Lock Record的空间,用于存储锁的Mark Word的拷贝。然后通过CAS操作尝试将对象头的Mark Word替换为指向Lock Record的指针。如果成功,线程获得锁;失败,则说明存在竞争,线程将自旋一段时间,不断尝试CAS操作直到成功或达到自旋上限。

轻量级锁到重量级锁

如果自旋超过一定次数(自旋阈值)仍未获得锁,轻量级锁将升级为重量级锁。此时,JVM会调用操作系统的互斥量(mutex)来实现线程阻塞和唤醒,这会导致线程挂起和恢复,开销较大。未获取到锁的线程会被阻塞,进入等待队列,而持有锁的线程执行完毕后,会唤醒队列中的下一个等待线程。

锁升级过程中的关键概念

CAS操作

CAS(Compare and Swap)操作是一种无锁算法,用于在多线程环境下实现原子操作。它比较内存中的值与预期值是否相等,如果相等则更新为新值,否则不做任何操作。在轻量级锁的获取过程中,CAS操作用于尝试将对象头的Mark Word替换为指向Lock Record的指针。

自旋锁

自旋锁是一种轻量级的锁机制,当线程尝试获取锁失败时,它不会立即被阻塞,而是会在一个循环中不断尝试获取锁,直到成功或达到某个条件(如自旋次数上限)。自旋锁避免了线程切换的开销,但在高竞争场景下可能会导致CPU资源的浪费。

自适应自旋

自适应自旋的基本思想是根据锁的争用情况,决定线程是否应该自旋等待,以及自旋等待的时间。JVM会根据历史数据动态调整自旋的次数,以减少不必要的自旋开销。

锁升级的意义

锁的升级过程是为了提高多线程环境下的性能和吞吐量,减少同步操作的开销,并尽量避免线程切换的开销。在大多数情况下,锁是由单个线程持有的,如果直接使用重量级锁,会浪费资源。因此,JVM根据线程竞争的情况和锁的使用情况自动进行锁的升级和降级,以优化多线程程序的性能。

代码示例

以下是一个简单的代码示例,展示了synchronized锁的升级过程:

public class SynchronizedExample {  
    // 对象锁示例  
    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) {  
        SynchronizedExample example = new SynchronizedExample();  
        Thread t1 = new Thread(() -> example.method1(), "Thread-1");  
        Thread t2 = new Thread(() -> example.method2(), "Thread-2");  
        t1.start();  
        t2.start();  
    }  
}

Java中,synchronized升级是指在不同的场景下,的实现方式会有所不同,以提高性能和并发控制的效率。下面是synchronized升级详解: 1. 偏向(Biased Locking):当一个线程访问同步块时,首先会尝试获取偏向。如果偏向未被其他线程占用,则当前线程会获得偏向,并标记为偏向线程ID。这样,在后续进入同步块时,无需再进行的竞争,提高了性能。只有当其他线程尝试获取偏向时,才会撤销偏向状态。 2. 轻量级(Lightweight Locking):当多个线程尝试竞争同一个时,偏向升级为轻量级。轻量级使用CAS操作来实现加和解,避免了线程阻塞和唤醒的开销。如果CAS操作失败,表示存在竞争,升级为重量级。 3. 重量级(Heavyweight Locking):当轻量级竞争失败时,升级为重量级。重量级使用操作系统的互斥量(Mutex)来实现,被阻塞的线程会进入等待状态,直到被释放。重量级的竞争会导致线程的上下文切换和调度开销增加,性能较低。 synchronized升级过程是根据实际情况和竞争情况动态进行的。在大多数情况下,升级是逐级升级的,即从偏向到轻量级,再到重量级。这种升级机制是为了在减少竞争时提供更好的性能,并在存在竞争时保证线程安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值