Synchronized的原理及自旋锁,偏向锁,轻量级锁,重量级锁的区别

举报
赵KK日常技术记录 发表于 2023/06/29 22:20:04 2023/06/29
【摘要】 1. 引言互联网的快速发展带来了大量并发访问和多线程的需求,因此实现线程安全的同步机制变得至关重要。在Java语言中,Synchronized关键字是实现同步的重要手段之一。本文将首先介绍Synchronized的原理,然后详细解析自旋锁、偏向锁、轻量级锁和重量级锁的区别。以下是一个Java代码示例,演示了Synchronized的使用和锁的类型:```javapublic class Sy...

1. 引言
互联网的快速发展带来了大量并发访问和多线程的需求,因此实现线程安全的同步机制变得至关重要。在Java语言中,Synchronized关键字是实现同步的重要手段之一。本文将首先介绍Synchronized的原理,然后详细解析自旋锁、偏向锁、轻量级锁和重量级锁的区别。

以下是一个Java代码示例,演示了Synchronized的使用和锁的类型:

```java
public class SynchronizedExample {
    private int count = 0;
    private final Object lock = new Object();

    // 使用Synchronized修饰方法
    public synchronized void increment() {
        count++;
    }

    // 使用Synchronized修饰代码块
    public void decrement() {
        synchronized (lock) {
            count--;
        }
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();
        
        // 创建多个线程并发访问共享资源
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.decrement();
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + example.count);
    }
}
```

在上述代码中,`increment()`方法使用Synchronized修饰,这表示在同一时间只允许一个线程访问该方法,防止并发冲突。`decrement()`方法则使用Synchronized代码块,指定了一个对象作为锁。在进行count--操作时,需要先获取该对象的锁才能执行,保证了线程安全性。

在`main()`方法中,我们创建了两个线程,一个线程调用`increment()`方法对count进行累加,另一个线程调用`decrement()`方法对count进行递减。通过多个线程并发地访问共享资源count,可以观察到Synchronized的保护效果。

需要注意的是,自旋锁、偏向锁、轻量级锁、重量级锁的选择和优化是由JVM自动完成的,开发者只需要使用Synchronized关键字,并没有直接控制锁的类型。JVM会根据具体的场景和竞争情况来选择合适的锁机制进行优化。以上只是对不同锁类型的概念和特点的简单解释。

2. Synchronized的原理
Synchronized是Java语言提供的一种内置锁机制,它可以保证在同一时刻只有一个线程可以进入被Synchronized修饰的代码块或方法。其原理是使用对对象的加锁和解锁操作来实现互斥访问,保证线程的安全性。

Synchronized在进入和退出时会自动地获取和释放对象的监视器锁(也称为内置锁)。当一个线程调用Synchronized修饰的代码块或方法时,会首先尝试获取对象的监视器锁,如果锁已经被其他线程获取,则当前线程会进入阻塞状态,直到锁被释放。当锁被释放后,处于阻塞状态的线程会被唤醒,然后竞争获取锁。只有获得锁的线程才可以执行Synchronized修饰的代码块或方法。

3. 自旋锁
自旋锁是一种等待机制,它使用循环来重复获取锁。当线程尝试获取锁时,如果锁已经被其他线程占用,那么线程会处于忙等待状态,循环不断地检查锁是否被释放。自旋锁可以减少线程切换的开销,适用于锁的占用时间很短的情况。然而,如果锁的占用时间较长,使用自旋锁可能会导致CPU资源的浪费。

4. 偏向锁
偏向锁是一种针对单线程访问的优化手段。当一个线程第一次访问Synchronized修饰的代码块或方法时,偏向锁会将对象头的标记位设置为偏向锁,并将当前线程的ID存储在对象头中。以后,当同一个线程再次访问该代码块或方法时,它可以直接获取偏向锁,无需进行任何同步操作。

偏向锁适用于无竞争的场景,可以降低同步操作的性能消耗。然而,当多个线程竞争同一个锁时,偏向锁会被撤销,升级为轻量级锁。

5. 轻量级锁
轻量级锁是一种基于CAS(Compare And Swap)操作的锁实现。当多个线程竞争同一个锁时,轻量级锁使用CAS操作来尝试获取锁。如果CAS操作成功,则线程获取到锁,可以继续执行。如果CAS操作失败,表示有其他线程正在竞争该锁,此时线程会膨胀为重量级锁。

轻量级锁不会引起线程的阻塞和唤醒操作,减少了线程的上下文切换开销,适用于锁的竞争不激烈的情况。然而,如果锁的竞争激烈,多个线程在循环中一直尝试获取锁,会消耗大量的CPU资源。

6. 重量级锁
重量级锁是一种传统的锁实现,它使用了操作系统提供的互斥量(Mutex)来实现锁的功能。当一个线程尝试获取重量级锁时,如果锁已经被其他线程占用,则线程会阻塞,进入到操作系统的内核态。当锁被释放后,其他线程才可以被唤醒进入竞争状态。

重量级锁可以保证锁的公平性,但是线程的阻塞和唤醒操作会导致较大的性能开销。因此,在高并发和多线程竞争激烈的场景下,使用重量级锁可能影响程序的性能。

7. 结论
在多线程编程中,实现线程安全是一个关键问题。Synchronized关键字作为Java语言提供的内置锁机制,在保证线程安全的同时,还提供了方便的语法支持。

自旋锁、偏向锁、轻量级锁和重量级锁是实现同步的不同机制,在不同情况下具有各自的优劣势。自旋锁适用于锁的占用时间较短的情况,可以减少线程切换的开销;偏向锁适用于无竞争的情况,可以降低同步操作的性能消耗;轻量级锁适用于锁的竞争不激烈的情况,减少了线程的上下文切换开销;重量级锁适用于锁的竞争激烈的情况,但是可能影响程序的性能。

综上所述,通过合理选择和使用不同的同步机制,可以根据具体的应用场景和需求,实现线程安全并提高程序的性能。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。