synchronized前生今世「与volatile区别 原理」

在这里插入图片描述

简介


Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象Object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍可以访问该object中的非加锁代码块。

使用

锁代码块

synchronized(*.class) : 锁住该类,所有访问该类的线程实例,一次只有一个可以执行
synchronized(this): 是加在对象上,只有这个对象不能访问,该对象所有锁this的地方都会锁住
synchronized(非this) : 非this锁的不是整个对象,不会导致对象其他地方锁住,比如锁String对象(或者全局变量),他在内存是常量池,只有一个

锁方法

普通方法: 是锁住对象,即调用当前示例对象的方法是都会锁住
静态方法: 是锁住类,即调用类的的静态方法时候都会锁住

原理

image.png

从上面我们可以看出:synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

在执行monitorenter时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。
对象锁的的拥有者线程才可以执行 monitorexit 指令来释放锁。在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放,其他线程可以尝试获取锁。

如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
对象头里面标识syschornized锁类型。
在等待队列获取Monitor,为0的时候加一成功则获取锁。
等待时候锁会升级 无锁->偏向锁->轻量级锁->重量级锁(JDK1.6以前只有重量锁,后来锁机制被优化了)。

Monitor是对象独有,静态(监视器是Class类实例)只有一份,非静态(每个实例对象都有监视器)

注意:

对象头的mark word标识为10, 指针地址会指向monitor对象
本质就是。static锁住的类锁,对象头指针指向的是同一个monitor

锁升级

原理

偏向锁升级轻量级锁:当一个对象持有偏向锁,一旦第二个线程访问这个对象,如果产生竞争,偏向锁升级为轻量级锁。

轻量级锁升级重量级锁:一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。

偏向锁

多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让其获得锁的代价更低而引入了偏向锁。

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

如果测试成功,表示线程已经获得了锁。
如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成01(表示当前是偏向锁)。
如果没有设置,则使用CAS竞争锁。
如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

轻量级锁

引入轻量级锁的主要目的是在多线程竞争不激烈的情况下,通过CAS竞争锁,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

重量级锁

重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。

和volatile区别

  • volatile 关键字是线程同步的轻量级实现,所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

和ReentrantLock区别

synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API

synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。
ReentrantLocksynchronized 增加了一些高级功能

相比synchronizedReentrantLock增加了一些高级功能。主要来说主要有三点:

  • 等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
  • 可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
  • 可实现选择性通知(锁可以绑定多个条件): synchronized关键字wait()notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口newCondition()方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杭州水果捞|Java毕业设计成品

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

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

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

打赏作者

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

抵扣说明:

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

余额充值