synchronized

synchronized

  1. 并发编程的三个问题:并发性,原子性,有序性

  2. 多线程并发时候可能出现可见性问题:就是在多线程中,在线程1中修改了变量a,在线程2中看不到同一个变量a的修改

  3. Synchonized可见性:Synchonized会把工作内存的变量清空,让我们去使用主存,因为Synchonized中会使用lock,而对一个变量使用lock操作,将会清空工作内存中变量的值(当前线程工作内存变量值清空了,下次就只能去主存中去数据了),而对于一个变量执行unlock操作之前,必须把此变量同步到主存中

  4. Synchonized锁的原理(指令级别):Synchonized会在自己包裹的代码执行前加monitorenter,在执行结束后使用monitorexit释放锁
    Synchonized锁的原理.png
  1. Synchonized原子性:加了Synchonized并不能防止指令重排,只是保证在同一个方法里面的操作是原子性的

  2. 为什么要重排序呢:这是cpu做的优化,目的是提高程序执行的效率,我们是控制不了的

  3. Synchonized有序性之不允许重排序的情况:

    1. 写后读:int a =1;int b = a这种情况是不允许重排序的,因为b是依赖于a的,如果重排先执行int b =a 再执行int a = 1,那么就是错误的,a都还没有值,又怎么赋值给b呢,所以这种情况是禁止重排序的

    2. 写后写:int a = 1;int a = 2这种情况也是不允许重排的,因为换了位置后,结果就不一样了

    3. 读后写:int a = 1;int b = a ;int a = 2这种情况也不能重排,因为调换位置后结果就不是我们想要的了

  4. Synchonized有序性之允许指令重排的情况:比如int a =1;int b = 2; int c = a+ b;那么此时int a = 1和int b = 2是可以重排的,因为不影响c的结果

  5. Synchonized保证有序性的意思是,不管编译器和cpu如何重排序,必须保证在单线程情况下,程序的结果是正确的

  • Synchonized可重入性

    1. 可重入:一个线程可以多次执行Synchonized重复获取同一把锁

    2. Synchonized的锁对象中有一个计数器(recursions变量)会记录线程获得几次锁

    3. 多个线程时,如果Runnable资源被Synchonized锁住了,那么当一个线程去执行的时候判断Synchonized的计数器值,只有计数器为0才能进入,证明抢到了这把锁,否则证明该锁已经被别人抢到了,我只能等待了;

    4. Synchonized可重入性的好处:
      死锁问题.png
  • 其他

    1. Synchonized是不可中断的,就是一个线程a获得了锁,其他线程必须等a执行完后才能执行自己的操作

    2. Synchonized修饰方法时,同步方法在反汇编后,会增加ACC_SYNCHRONIZED 修饰。会隐式调用monitorenter和 monitorexit。在执行同步方法前会调用monitorenter,在执行完同步方法后会调用monitorexit。

    3. Synchonized和lock的区别:

      1. Synchonized是关键字,lock是接口

      2. Synchonized会自动释放锁,lock要手动释放

      3. Synchonized是不可中断的,lock可以中断也可以不中断

      4. Synchonized不知道线程是否拿到锁,lock可以知道线程是否获得了锁

      5. Synchonized可以锁住方法和代码块,lock只能锁代码块

      6. Synchonized是非公平锁,ReentrantLock可以控制是否是公平锁,ReentrantLock默认是非公平的,创建的时候传入true就是公平锁了

    4. monitor是重量级锁:执行同步代码块,没有竞争到锁的对象会park()被挂起,竞争到锁的线程会unpark()唤醒。这个时候就会存在操作系统用户态和内核态的转换,这种切换会消耗大量的系统资源。所synchronized是Java语 言中是一个重量(Heavyweight)的操作。

    5. 为什么monitor中操作系统用户态和内核态的转换会消耗大量的资源呢:因为用户态切换至内核态需要传递许多变量,同时内核还需要保护好用户态在切换时的一些寄存器 值、变量等,以备内核态切换回用户态。这种切换就带来了大量的系统资源消耗,这就是在synchronized未优化之前,效率低的原因。

    6. CAS:Compare And Swap(比较相同再交换),CAS可以将比较和交换转换为原子操作,这个原子操作直接由CPU保证。CAS可以保证共享变量赋值时的原子操作。CAS操作依赖3个值:内存中的值V,旧的预估值X,要修改的新值B,如果旧的预估值X等于内存中的值V,就将新的值B保存到内存中。
      CAS原理.png
  1. CAS获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。结合CAS和volatile可以实现无锁并发,适用于竞争不激烈、多核 CPU 的场景下,因为单核的话,多线程是cpu切换实现的,那其他线程被放在就绪队列里面的,根本没有执行,所以用不到CAS。

    1. 因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一。

    2. 但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响。

  2. 所以CAS就是一种乐观锁,而synchronized是一种悲观锁

  3. 对象的布局:对象头,实例数据,对齐填充

    1. 对象头由两部分组成,一部分用于存储自身的运行时数据,称之为 Mark Word,另外一部分是类型指针,及对象指向它的类元数据的指针。当一个线程尝试访问synchronized修饰的代码块时,它首先要获得锁,那么这个锁到底存在哪里呢?是存在锁对象的对象头中的。

    2. Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄(经过15次的GC后就会进入老年代)、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。

synchronized锁升级过程:

  • 偏向锁(当只有一个线程获取锁时):这个锁会偏向于第一个获得它的线程,会在对象头存储锁偏向的线程ID,以后该线程进入和退出同步块时只需要检查是否为偏向锁、锁标志位以及ThreadID即可。

    1. 当线程第一次访问同步块并获取锁时,偏向锁处理流程如下:虚拟机将会把对象头中的标志位设为“01”,即偏向模式。

    2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何 同步操作,偏向锁的效率高。

    3. 偏向锁的撤销(当有线程竞争时,就不能使用偏向锁了,就要撤销偏向锁):1.偏向锁的撤销动作必须等待全局安全点(一个暂停的时刻,非常短);2.暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态3.撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态

    4. synchronized默认是开启的轻量级锁,延时4秒后才会打开偏向锁,为什么会延时呢?因为在synchronized代码块执行前,还会进行其他的操作,可能会用到synchronized,可能是并发的,所以我们不能一开始就设置它为偏向锁,---注意: 可以手动关闭偏向锁延时

    5. 偏向锁是在只有一个线程执行同步块时进一步提高性能,适用于一个线程反复获得同一锁的情况。偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问比如线程池,那偏向模式就是多余的。

    6. 如果默认就开启偏向锁的话,那么即使没执行到synchronized代码块,对象也会有偏向锁,此时为匿名偏向锁,对象头中是没有线程id的,当真正执行到synchronized代码块时,才把线程id写进去

  • 轻量级锁:当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁

    1. 引入轻量级锁的目的:引入轻量级锁的目的是在多线程交替执行(交替执行,而不是并发)同步块的情况下,尽量避免重量级锁引起的性能消耗,但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以轻量级锁的出现并非是要替代重量级锁。

    2. 轻量级锁升级为重量级锁之前会默认使用自旋锁自旋10次,如果10次内能拿到锁,就不升级为重量级锁了,否则升级为重量级锁,后面又有了自适应的自旋锁,此时自旋的次数,和单次的时间就由虚拟机决定了,而不是固定的

  • 锁相关

    1. 锁消除:是指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

    2. 锁粗化:JVM会探测到一连串细小的操作都使用同一个对象加锁,将同步代码块的范围放大,放到这串操作的外面,这样只需要加一次锁即可。

    3. 优化:1.减少synchronized的代码块的作用范围,代码越少,执行的速度越快,那么发生竞争的概率就越小;2.降低synchronized锁的颗粒度,比如hashtable和concurrentHashMap,前者锁整个表,后者只锁链表和红黑树的首节点,肯定后者的效率更高;读写分离:读取时不加锁,写入和删除时才加锁

  • 用户态和内核态:
  1. 由 synchronized锁优化联想到用户态与内核态区别_未知的风fly-CSDN博客

  2. 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 目录介绍 5.0.0.1 线程池具有什么优点和缺点?为什么说开启大量的线程,会降低程序的性能,那么该如何做才能降低...
    杨充211阅读 408评论 0 3
  • 1. 多线程 新建状态: 一个新产生的线程从新状态开始了它的生命周期。它保持这个状态直到程序 start 这个线程...
    无痕25阅读 275评论 0 0
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,681评论 0 11
  • 创建线程的方式 一般来说,CachedTheadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回...
    夜雨听风_b3d5阅读 251评论 0 0
  • Java并发编程 并发一定比串行快么? 这个问题肯定是错的,并发比串行慢的原因在于:线程有创建和上下文切换的开销 ...
    Java机械师阅读 346评论 0 6