synchronized总结

synchronized学习:

什么是synchronized?

  • Synchronized是Java中的关键字,由JVM实现的。
  • Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
  • 原子性:可以确保线程互斥的访问同步代码;
  • 可见性:保证共享变量的修改能及时可见,通过Java内存模型中的,对一个变量unlock操作前,必须同步到主内存中;如果对一个变量进行lock操作,则会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值。来保证可见性。
  • 有序性:有效解决重排序问题,即:一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作。

使用场景:

synchronized使用场景

synchronized 原理:

1、synchronized修饰的代码块中。
synchronized(this){
	sout("xXXX");
}
  • 查看synchronized修饰过的代码块编译后的字节码,会发现被synchronized修饰过的代码块前后会生成两个字节码指令:monitorenter、monitorexit;
  • 这两个字节码指令的含义:当JVM执行到monitorenter和monitorexit两个指令实现同步。
    • monitorenter : 每个对象都与一个监视器锁(monitor)相关联,monitor才会被锁定。线程执行monitorenter指令时会尝试获取monitor的所有权。
      • 每个对象维护者一个记录着被锁定次数的计数器,对象未被锁定时,计数器为0。线程进入monitor(执行monitorenter指令)时,计数器设置为1;同一个线程再次获取到该对象锁时,计时器会再加1;当其他线程尝试获取monitor时,会阻塞,直至计数器为0。
    • monitorexit:执行monitorexit的线程必须是Object所对应的monitor所有者。指令执行时,monitor进入数减1,如果减1后进入数为0,则线程退出monitor,不再占有monitor。此时其他线程可以尝试获取monitor所有权。
      总:synchronized底层通过一个monitor的对象来完成,另外wait和notify等方法也是依赖于monitor对象的,也就是说为什么只有同步方法块和中才能调用wait和notify。
2、在同步方法中。
synchronized void mothod(){
	sout("XXXXXXX");
}

反编译后结果中没有monitorenter和monitorexit指令,常量池中多了个ACC_SYNCHRONIZED标识符。

当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程会先获取monitor,获取成功后方能执行方法体,方法执行完再释放monitor。方法执行期间其他任何线程都无法再获取monitor对象。

monitor监视器

  • monitor 可以理解为一种同步工具,或者同步机制,通常被描述成一个对象。
  • 操作系统中管程的概念,具体实现原理ObjectMonitor
    在这里插入图片描述
  • 操作系统的管程
    • 管程(monitor,监视器),是一种程序结构,结果内部的多个子程序(对象模块)形成多个工作线程互斥访问共享资源。其实现在一个时间点,最多只有一个线程在执行管程的某个子程序。
  • ObjectMonitor 数据结构:在JVM中monitor(管程)是由ObjectMonitor实现的。
ObjectMonitor(){
_header=NULL;
_count=0;//记录owner线程获取锁的次数
_waiters=0;
_recursions=0;//锁重入的次数
_object=NULL;
_owner=NULL;//指向持有ObjectMonitor对象的线程。
_WaitSet=NULL;//处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock=0;
_Responsible=NULL;
_succ =NULL;
_cxq=NULL;
FreeNext=NULL;
_EntryList=NULL;//处于等待锁block状态的线程,会被加入到该列表
_SpinFrq=0;
_SpinClock=0;
OwnerlsThread=0;
}

Monitor 工作机理:

Java monitor工作机理

  • 想要获取到monitor的线程,首先进入到_EntryList队列。
  • 当某线程获取到对象的monitor后,进入到Owner区域,设置为当前线程,计数器count加1;
  • 如果线程调用wait()方法,则进入waitset队列,同时会释放monitor锁,将owner赋值为null,count自减1,进入waitset队列阻塞等待。
  • 如有其他线程调用notify()/notifyAll(),会唤醒waitset中的某个线程,该线程再次尝试获取monitor锁,成功则进入owner区域。
  • 同步方法执行完毕,线程退出临界区,会将owner置为null,并释放监视锁。
synchronized(object){//进入_EntryList队列
method();
object.wait();//进入_WaitSet队列
}

可重入锁,synchronized可重入?

  • 可重入:指的是当前线程获取到当前锁后,如果后续操作仍然需要获取该对象锁时,可以不用再次重新获取,可以直接操作该对象(共享资源)。
  • 可重入性是为了解决自己锁死自己的情况。当一个类的同步方法调用另外一个同步方法时,如果synchronized不支持可重入,进入方法1时当前线程已经获取到了锁,而在方法1中执行方法2时又尝试去获取锁,此时如果不支持重入,就需要等待方法1释放锁,把自己阻塞,导致自己锁死自己。
  • synchronized的可重入是在执行monitorenter指令时,如果对象没有锁定,或当前线程已经拥有了这个对象的锁,则把计数器加1。通过此方法实现可重入性。

synchronized锁的竞争与升级?(Java对原生锁的优化)

  • 1.6之前,monitor的实现完全依赖于底层操作系统的互斥锁来实现,也即是monitorenter和monitorexit;Java的线程与操作系统的原生线程有映射关系,如果要将一个线程进行阻塞或唤起均需要操作系统协助,需要从用户态切换到内核态来执行,这种切换十分昂贵。
  • 优化:
    • 自旋锁:即是线程进行阻塞操作之前先让线程自旋一段时间,可能在等待期间,别的线程已经解锁,这样就不需要再让线程执行阻塞操作,避免用户态切换到内核态。
    • 对monitor的三种不同实现:偏向锁、轻量级锁、重量级锁。这三种锁是JDK优化synchronized的运行,当jvm检测到不同的金正状态时会自动切换到合适的锁实现,实现锁的升级和降级。
      • 锁升级、降级的过程:
        • 在没有竞争出现时,默认使用偏向锁,JVM会利用CAS操作,在对象头上的MarkWord部分设置线程ID,来表示偏向于当前线程,并不涉及真正的互斥锁。在很多场景下,大部分对象生命周期中最多会被一个线程锁定,使用偏向锁来降低无竞争开销。
        • 当有另外一个线程试图锁定某一个被偏向锁锁过的对象,JVM会自动撤销偏向锁,切换为轻量级锁;轻量级锁依赖CAS操作MarkWord来试图获取锁,如果重试成功,就是用普通的轻量级锁,否则继续升级为重量级锁。
  • Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用偏向锁优先同一线程再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就会短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为重量级锁。

synchronized是公平锁还是非公平锁?

  • 非公平锁

synchronized和volatile关键字的区别?

synchronized关键字和volatile关键字不是对立的是互补的。

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

synchronized与lock的区别?

  • synchronized是关键字是有jvm底层实现,什么都帮我们做了;lock是一个接口,JDK层面有很丰富的API。
  • synchronized会自动释放锁;Lock则需要我们手动释放锁。
  • synchronized是不可中断的;Lock则是可以中断的。
  • Lock可以知道是否获取到锁,synchronized则不能。
  • synchronized可以锁定方法和代码块;Lock只能锁定代码块。
  • Lock可以使用读锁提高多线程的读效率。
  • synchronized是非公平的;ReentrantLock可以控制是否公平锁。

未完待续~

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
synchronized是一种关键字,可以实现线程之间的同步和互斥。在Java中,synchronized是可重入的,也就是说同一个线程可以多次获取同一把,或者在调用自己类中的其他synchronized方法/块时不会阻碍线程的执行。这意味着同一个线程可以连续地获取同一个对象的,而不会造成死或阻塞。这种可重入性是由Java虚拟机自动管理的,通过监视器(monitor)来实现。当线程第一次获取时,会在的持有计数器上加1,之后每次成功获取,计数器就会增加。只有当计数器归零时,才会被完全释放。因此,同一个线程在获取后,可以再次获取同一把而不会被阻塞。这种特性使得在synchronized方法/块中调用其他synchronized方法/块成为可能,而不会出现死或死循环的情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [synchronized总结](https://blog.csdn.net/weixin_39559282/article/details/114273936)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [synchronized——可重入性](https://blog.csdn.net/SmuEdward/article/details/54629771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

benboerdong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值