浅谈JVM内置锁synchronized

文章详细介绍了多线程环境下同步器的重要性,通过同步机制解决线程并发安全问题,特别是Java中的synchronized关键字及其工作原理,包括内置锁、Monitor对象、锁优化技术如轻量级锁和偏向锁等。此外,还提到了同步方法和同步代码块的使用,以及锁对象的选择策略。
摘要由CSDN通过智能技术生成

设计同步器的意义

  • 多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:对象、变量、文件等。
    • 共享:资源可以由多个线程同时访问
    • 可变:资源可以在其生命周期内被修改

        引出的问题:

             由于线程执行的过程是不可控的,所以需要采用同步机制来协同对对象可变状态的访问!

        如何解决线程并发安全问题?

            实际上,所有的并发模式在解决线程安全问题时,采用的方案都是序列化访问临界资源。即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问

        Java 中,提供了两种方式来实现同步互斥访问:synchronized 和 Lock。同步器的本质就是加锁。

        加锁目的:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问)

        不过有一点需要区别的是:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享性,不会导致线程安全问题。synchronized原理详解

        synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问,是可重入的。

        加锁的方式:

  1. 同步实例方法,锁是当前实例对象
  2. 同步类方法,锁是当前类对象
  3. 同步代码块,锁是括号里面的对象

synchronized底层原理

        synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁,性能较低。

        当然,JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销,内置锁的并发性能已经基本与Lock持平。

        内置锁的实现原理是,每个Java对象都有一个与之相关联的监视器锁(也称为内置锁)。当线程要访问被锁定的代码块时,它必须先获得与该对象相关联的监视器锁。如果另一个线程已经持有了这个锁,则当前线程就会阻塞等待,直到该锁被释放。

        内置锁的优点是实现简单,使用方便。同时,由于内置锁是JVM级别的锁,因此可以确保多个线程在同一时间只能访问一个对象的同步代码块。但是,内置锁也存在一些缺点。首先,在高并发场景下,内置锁可能会导致性能问题。其次,内置锁只能基于对象级别进行同步,无法对代码块进行细粒度控制。

        synchronized关键字被编译成字节码后会被翻译成monitorenter 和 monitorexit 两条指令分别在同步块逻辑代码的起始位置与结束位置。

 

内部锁的使用范式


1.同步实例方法

 int count;
    synchronized void syncA() {
        count++;
    }

等效于:

int count;
    void syncA() {
        synchronized (this) {
            count++;
        }
    }

上述两个等效的同步实例方法都是同步在this当前对象。

2.同步静态方法(类方法)

public class Foo {
    static int classCount;
    static synchronized void syncB() {
        classCount++;
    }
}

等效于:

public class Foo {
    static int classCount;
    static void syncB() {
        synchronized (Foo.class) {
            classCount++;
        }
    }

}

上述两个同步的类方法都是同步在类对象Foo.class上面,类对象也是对象。

实际中我们也会经常这样使用:

    private final Object lock = new Object();
    
    int count;
    void syncA() {
        synchronized (lock) {
            count++;
        }
    }

作为锁对象(锁句柄)使用的lock要声明为不可变对象,因为对多个线程来说,只有同步在相同的锁(同一把锁)上才有意义,才能保证共享数据的安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白橘大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值