Java关键字synchronized原理与锁的状态实例分析

发布时间:2022-08-10 13:56:53 作者:iii
来源:亿速云 阅读:194

Java关键字synchronized原理与锁的状态实例分析

引言

在多线程编程中,线程安全是一个非常重要的问题。Java提供了多种机制来保证线程安全,其中synchronized关键字是最常用的一种。synchronized关键字可以用于方法或代码块,确保同一时间只有一个线程可以执行被synchronized修饰的代码。本文将深入探讨synchronized关键字的原理,并结合实例分析锁的状态。

1. synchronized关键字的基本用法

synchronized关键字可以用于修饰方法或代码块,确保同一时间只有一个线程可以执行被synchronized修饰的代码。

1.1 修饰实例方法

synchronized修饰实例方法时,锁对象是当前实例对象(this)。

public class SynchronizedExample {
    public synchronized void method() {
        // 同步代码块
    }
}

1.2 修饰静态方法

synchronized修饰静态方法时,锁对象是当前类的Class对象。

public class SynchronizedExample {
    public static synchronized void staticMethod() {
        // 同步代码块
    }
}

1.3 修饰代码块

synchronized还可以用于修饰代码块,锁对象可以是任意对象。

public class SynchronizedExample {
    private final Object lock = new Object();

    public void method() {
        synchronized (lock) {
            // 同步代码块
        }
    }
}

2. synchronized关键字的原理

synchronized关键字的实现依赖于Java对象头中的Mark WordMonitor机制。

2.1 对象头与Mark Word

在Java中,每个对象都有一个对象头,对象头包含两部分信息:Mark WordKlass PointerMark Word用于存储对象的哈希码、GC分代年龄、锁状态等信息。

在32位JVM中,Mark Word的结构如下:

锁状态 25bit 4bit 1bit (偏向锁) 2bit (锁标志位)
无锁 对象的哈希码 对象分代年龄 0 01
偏向锁 线程ID Epoch 1 01
轻量级锁 指向栈中锁记录的指针 00
重量级锁 指向互斥量(Monitor)的指针 10
GC标记 11

2.2 Monitor机制

Monitor是Java中实现同步的基础。每个Java对象都与一个Monitor相关联,Monitor可以理解为一个锁对象。当一个线程进入synchronized代码块时,它会尝试获取对象的Monitor,如果成功获取,则进入同步代码块执行;如果失败,则进入阻塞状态,直到其他线程释放Monitor

Monitor的实现依赖于操作系统的互斥量(Mutex)和条件变量(Condition Variable)。在JVM中,Monitor的实现通常是通过ObjectMonitor类来完成的。

3. 锁的状态

Java中的锁有四种状态:无锁、偏向锁、轻量级锁和重量级锁。锁的状态会根据竞争情况动态升级。

3.1 无锁

无锁状态是指对象没有被任何线程持有锁。在无锁状态下,Mark Word中存储的是对象的哈希码和分代年龄。

3.2 偏向锁

偏向锁是为了在没有竞争的情况下减少同步开销而引入的。当一个线程第一次获取锁时,JVM会将锁状态设置为偏向锁,并将Mark Word中的线程ID设置为当前线程的ID。之后,如果同一个线程再次请求锁,JVM会直接允许其进入同步代码块,而不需要进行任何同步操作。

偏向锁的优点是减少了无竞争情况下的同步开销,但在有竞争的情况下,偏向锁会升级为轻量级锁。

3.3 轻量级锁

轻量级锁是通过CAS(Compare-And-Swap)操作来实现的。当一个线程尝试获取锁时,JVM会尝试将Mark Word中的指针替换为指向当前线程栈中锁记录的指针。如果替换成功,则获取锁成功;如果失败,则说明有其他线程竞争锁,此时锁会升级为重量级锁。

轻量级锁的优点是减少了线程阻塞的开销,但在高竞争的情况下,轻量级锁会频繁升级为重量级锁,导致性能下降。

3.4 重量级锁

重量级锁是通过操作系统的互斥量(Mutex)来实现的。当一个线程获取重量级锁时,如果锁已经被其他线程持有,则该线程会进入阻塞状态,直到锁被释放。

重量级锁的优点是适用于高竞争的情况,但缺点是线程阻塞和唤醒的开销较大。

4. 锁的状态转换

锁的状态会根据竞争情况动态升级,但不会降级。锁的状态转换过程如下:

  1. 无锁 -> 偏向锁:当一个线程第一次获取锁时,JVM会将锁状态设置为偏向锁。
  2. 偏向锁 -> 轻量级锁:当有另一个线程尝试获取锁时,偏向锁会升级为轻量级锁。
  3. 轻量级锁 -> 重量级锁:当轻量级锁的CAS操作失败时,锁会升级为重量级锁。

5. 实例分析

下面通过一个实例来分析锁的状态转换过程。

public class LockStateExample {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 线程1获取锁
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 1 holds the lock");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 线程2获取锁
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 2 holds the lock");
            }
        });

        thread1.start();
        Thread.sleep(100); // 确保线程1先获取锁
        thread2.start();

        thread1.join();
        thread2.join();
    }
}

在这个例子中,thread1首先获取锁并进入同步代码块,thread2随后尝试获取锁。由于thread1持有锁,thread2会进入阻塞状态。此时,锁的状态会从无锁升级为偏向锁,再升级为轻量级锁,最后升级为重量级锁。

6. 总结

synchronized关键字是Java中实现线程同步的重要机制。通过synchronized关键字,可以确保同一时间只有一个线程可以执行被synchronized修饰的代码。synchronized关键字的实现依赖于Java对象头中的Mark WordMonitor机制。锁的状态会根据竞争情况动态升级,从无锁到偏向锁,再到轻量级锁,最后到重量级锁。

在实际开发中,理解synchronized关键字的原理和锁的状态转换过程,有助于我们更好地编写高效、线程安全的代码。

推荐阅读:
  1. Java Synchronized 锁的实现原理与应用 (偏向锁,轻量锁,重量锁)
  2. 查询锁状态与处理

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java synchronized

上一篇:Windows server 2016服务器基本设置实例分析

下一篇:Vue中的echarts图表怎么实现loading效果

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》