java synchronized 原理_Java基础-Synchronized原理

在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。

CAS(Compare and Swap),用于在硬件层面上提供原子性操作,在Intel处理器中,比较并交换通过指令cmpxchg实现。比较是否和给定的数值一致,如果一致则修改,不一致则不修改

基础

Java中的每一个对象都可以作为锁。

对于同步方法,锁是当前实例对象。

对于静态同步方法,锁是当前对象的Class对象。

对于同步方法块,锁是Synchronized括号里配置的对象。

当一个线程视图访问同步代码块时,它首先必须得到锁,退出或排除异常时必须释放锁,那么锁在哪里呢?锁里面会存储什么信息呢?

同步的原理

JVM规范规定JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但是两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是方法的同步同样可以使用这两个指令来实现。

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

Java对象头

锁存在Java对象头里,如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头,在32位虚拟机中,一字宽等于四字节,即32bit

锁的升级

Java SE1.6 为了减少获得锁和释放锁锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态、偏向锁状态、轻量级锁状态、和重量级锁状态,它会随着竞争情况逐渐升级,锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁,这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

线程对锁的竞争

当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态来区分请求的线程:

Contention List:

所有请求锁的线程将会被首先放置到该竞争队列。

Entry List:

Contention List中那些有资格成为候选人的线程被移到Entry List

Wait Set:

那些调用wait方法被阻塞的线程被放置到Wait Set

OnDeck :

任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck

Owner:

获得锁的线程称为Owner

!Owner:

释放锁的线程

179fb7db5bac06336430be7aca770080.png

ContentionList

ContentionList并不是一个真正的Queue,而只是一个虚拟队列,原因在于ContentionList是由Node极其next指针逻辑构成,并不存在一个Queue的数据结构。ContentionList是一个后进先出(LIFO)的队列,每次新加入Node时都会在队头进行,通过CAS改变第一个节点的指针为新增节点,同时设置新增节点的next指向后续节点,而取得操作则发生在队尾。显然,该结构其实是个Lock-Free队列。

因为只有Owner线程才能从队尾取元素,也即线程出列操作无争用,当然也就避免了CAS的ABA问题。

EntryList

EntryList与ContentionList逻辑上同属等待队列,ContentionList会被线程并发访问,为了降低对ContentionList队尾的争用,而建立EntryList。Owner线程在unlock时会从ContentionList中迁移线程到EntryList,并会指定EntryList中的某个线程(一般为Head)为Ready(OnDeck)线程,Owner线程并不是把锁传递给OnDeck线程,只是把竞争锁的权利交给OnDeck,OnDeck线程需要重新竞争锁,这样做虽然牺牲了一定的公平性,但极大地提高了整体吞吐量,在Hotspot中把OnDeck的选择行为称之为“竞争切换”。

OnDeck线程获得锁后即变为owner线程,无法获得锁则会依然留住EntryList中,考虑到公平性,在EntryList中的位置不发生变化(依然在队头)。如果Owner线程被wait方法阻塞,则转移到WaitSet队列;如果在某个时刻被notify/notifyAll唤醒,则再次转移到EntryList。
————————————————
版权声明:本文为CSDN博主「贾温悦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_34603528/article/details/114209594

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中的synchronized关键字是用于实现线程同步的机制。当一个Java线程进入synchronized代码块时,它会尝试获取锁(也称为监视器锁)来保护同步代码块。如果锁已被其他线程持有,则该线程将阻塞,直到锁可用为止。 在Java中,每个对象都有一个关联的监视器,也称为内部锁。synchronized关键字可以用于方法和代码块,以便只有一个线程可以持有该对象的锁。这确保了同一时间只有一个线程可以访问共享资源,从而避免了多个线程同时修改共享资源导致的数据不一致问题。 当一个线程进入一个被synchronized关键字保护的代码块时,它会尝试获取锁,如果锁已被其他线程持有,则该线程会被阻塞。只有当锁被释放时,其他线程才能获得锁并继续执行同步代码块中的代码。这样可以确保同步代码块中的代码只有一个线程执行,避免了数据竞争和其他并发问题。 ### 回答2: Java是一种面向对象编程语言,具有良好的可移植性,广泛应用于互联网、移动设备和嵌入式设备等领域。在Java中,synchronized关键字是实现多线程同步的重要机制之一。 Java中的synchronized关键字是用来控制线程的访问权限的,它可以将语句块或方法声明为同步的,从而避免多个线程同时执行这些代码。synchronized代码块的基本语法是: synchronized(object){ //需要同步的代码块 } 其中,object表示需要锁定的对象。在synchronized代码块中,每次只有一个线程能够获得对象的锁定,其他线程则需要等待该锁释放后再进行访问。 synchronized关键字的原理是基于Java中的“互斥锁”(Mutex)机制实现的。Mutex是一种特殊的信号量,用于协调多个线程的访问。当一个线程需要访问某个对象时,它会试图获取该对象的互斥锁。如果该锁已经被其他线程占用,则该线程会被阻塞,直到该锁被释放后才能继续执行。 在Java中,对于每个对象都有一个相应的互斥锁,称为“内置锁”(Intrinsic Lock)或“监视器锁”(Monitor Lock)。当一个线程需要进入synchronized代码块时,它需要先获得该对象的内置锁,在执行完代码块后再释放该锁。 具体来说,内置锁由两部分组成:锁定状态和处于等待状态的线程队列。当一个线程需要获得某个对象的锁时,它会先判断该锁是否为空闲的。如果是,则该线程就会获得该锁;否则,该线程就会被加入到对象的线程队列中,并进入等待状态。当锁释放后,会通知等待队列中的线程,让它们重新竞争锁的所有权。 需要注意的是,synchronized关键字不仅可以用于同步代码块,还可以用于同步方法。在Java中,每个对象都有一个隐藏的锁定对象,当调用某个对象的同步方法时,该对象的锁定对象就会被锁定。这样,其他线程就无法访问该对象的其他同步方法和同步代码块,直到该线程执行完该方法并释放锁为止。 总之,在Java中,synchronized关键字是实现多线程同步的基本机制之一,它是基于内置锁和监视器锁实现的。通过锁定对象,它可以确保同一时间只有一个线程能够访问被保护的代码块或方法,从而避免多个线程之间的不必要竞争和冲突,确保程序的正确性和可靠性。 ### 回答3: Java中的synchronized是一种用于保护共享资源的关键字。当多个线程尝试访问同一个对象的synchronized代码块时,只有一个线程能够进入代码块,其他线程必须等待直到同步操作完成并释放锁定。 synchronized的实现原理涉及到Java中的对象头和Monitor,其中对象头包含了一些元数据和指向Monitor的指针,Monitor则包含了互斥锁(mutex lock)和等待队列(wait set)这两个关键元素。 当一个线程进入synchronized代码块时,它会尝试去获取Monitor的互斥锁,如果锁定成功则该线程可以执行代码块中的代码,如果锁定不成功则该线程就会进入等待队列并陷入阻塞状态。当一个线程完成synchronized代码块时,它会释放Monitor的互斥锁,并且唤醒所有在等待队列中的线程,这些线程会重新尝试去获取锁定并继续执行。 需要注意的是,在Java中,每个对象都有一个关联的Monitor,因此使用synchronized的时候,锁定的粒度是对象级别的。所以如果多个线程同时执行的是不同对象的同步方法,则它们之间不会产生竞争;反之,如果多个线程同时执行的是同一个对象的不同同步方法,则这些方法之间还是会存在竞争关系。 除了使用synchronized关键字之外,在Java中还可以使用Lock接口和Condition接口来实现线程的同步和互斥。不过无论是使用synchronized还是Lock接口,它们的本质都是基于Monitor和互斥锁的实现原理

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值