重新认识synchronized(下)


synchronized既保证原子性,又保证内存可见性,是一种线程同步的方式,是锁机制的一种java实现。synchronized的实现基于JVM底层,JVM是基于monitor实现的,而monitor的实现依赖于操作系统的互斥实现。

语义

synchronized语义是同步,但同步有两层含义:

  1. 互斥,即锁的特点。同一时间只能有一个线程持有监视器,因此一旦线程进入监视器保护的代码块(即临界区),其他线程是不允许监视器保护的代码块,直到前一个线程退出代码块。互斥阻止了其他线程看到对象不一致的状态,与原子性有相同的语义。
  2. 可见。synchronized保证进入同步代码块的线程,都可以看到由同一个锁保护的之前所有的修改效果。原因是:在释放监视器时(即退出同步代码块),会将工作内存中未映射到主内存的工作拷贝,强制刷新回主内容。在获取监视器是(即进入同步代码块时),监视器会使本地内存失效,强制从主内存拷贝到工作内存。

互斥保证在线程退出前,所有对象状态变更都对其他线程不可见;可见保证在线程进入同步代码块时,可以看到上一个线程对对象状态变更的最终状态。

线程安全与同步

线程安全表明在多线程环境中,不会有多个线程同时访问共享数据。
线程同步是线程访问类和实例字段变量,和其他共享资源的一种串行化行为,确保在同一时间只能有一个线程访问资源。举个栗子,春运火车票只剩下最后一张火车票,A,B都要抢这张火车票,怎么解决这个问题防止超卖呢?把资源保护起来,让A,B排队来买火车票。
线程安全是属性,线程同步是方式。

指令

synchronized同步代码块是通过monitorenter和monitorexit指令实现的,而synchronized同步方法是基于ACC_SYCHRONIZED标志,同步方法被调用时JVM会检查这个标志。monitorenter标记临界区的开始,线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权;monitorexit标记临界区的结束,线程执行到 monitorexit 指令时,将释放对象所对应的 monitor 的所有权。

1 public class SynchronizedMethod {
2     public synchronized void methodA() {
3         System.out.println("MethodA start");
4
5     }
6 }

将这段代码通过 javap -c 反编译一下,重点关注一下编译后的第3行和第13行。

 1 Compiled from "SynchronizedTest.java"
 2 public class com.memory.SynchronizedTest {
 3   public com.memory.SynchronizedTest();
 4     Code:
 5        0: aload_0
 6        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 7        4: return
 8
 9   public void methodA();
10     Code:
11        0: aload_0
12        1: dup
13        2: astore_1
14        3: monitorenter
15        4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
16        7: ldc           #3                  // String MethodA start
17        9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
18       12: aload_1
19       13: monitorexit
20       14: goto          22
21       17: astore_2
22       18: aload_1
23       19: monitorexit
24       20: aload_2
25       21: athrow
26       22: return
27     Exception table:
28        from    to  target type
29            4    14    17   any
30           17    20    17   any
31 }

锁的种类

java synchronized锁升级
JDK1.6中对synchronized优化引入了偏向锁,轻量级锁,重量级锁。锁的升级过程是单方向的,只允许从低到高升级,不允许降级。

重量级锁(Heavyweight Lock)是将程序运行交出控制权,将线程挂起,由操作系统来负责线程间的调度,负责线程的阻塞和执行。这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,消耗大量的系统资源,导致性能低下。

轻量级锁(lightweight Locking)是相对于重量级锁而言的,在synchronized实现中使用自旋的方式,实际是通过CPU自旋等待的方式替代线程切换,竞争的线程不会因此而阻塞,避免阻塞唤醒造成的CPU负荷。采用自旋的方式有利有弊,当锁占用的时间较短时,较少次数的自旋等待就可以获取锁;但在锁占用的时间较长时,自旋会白白浪费大量的CPU资源。因此自旋的次数有一定要在限定之内,自旋失败就会立即将锁升级为重量级锁,称为锁膨胀。

偏向锁(Biased Locking )从字面含义是这把锁是有私心的,会倾向于上次访问的线程。Hotspot的作者在他的论文《QRL-OpLocks-BiasedLocking》中阐述到,研究发现大多数情况下不存在多线程争夺共享资源,而且总是由同一线程多次获得,考虑到CAS (Compare-And-Swap)指令在获取Java监视器时会造成较大的CPU延迟,为了让线程获得锁的代价更低而引入了偏向锁。

锁标记的位置

64位虚拟机中,标记字段(Mark Word)中包含哈希吗(HashCode,存放31bits对象的hashcode值),GC分代年龄(Generational GC Age,4bits,因此分代年龄最高为15),偏向线程ID,偏向锁标记。
synchronized锁的四个状态:无锁状态,偏向锁,轻量级锁和重量级锁,在Mark Word中对应不同的字段。
java synchronized不同级别锁中的Mark Word

我是葛一凡,希望对你有用。

参考

  1. JSR 133 (Java Memory Model) FAQ
  2. Java内存模型(一)
  3. Java 锁机机制——浅析 Synchronized
  4. Biased Locking in HotSpot
  5. Java Synchronised机制
  6. 深入JVM锁机制1-synchronized
  7. 多线程之:偏向锁,轻量级锁,重量级锁
  8. JVM内部细节之一:synchronized关键字及实现细节(轻量级锁Lightweight Locking)
  9. java锁优化
  10. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
  11. 虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁)
  12. JVM内部细节之二:偏向锁(Biased Locking)
  13. 小议偏向锁
  14. synchronized、锁、多线程同步的原理是咋样的
  15. Java中的锁
  16. markOop.hpp
  17. 5 Things You Didn’t Know About Synchronization in Java and Scala
  18. 论文:The Smart Energy Management of Multithreaded Java Applications on Multi-Core Processors
时间: 2024-08-11 06:40:50

重新认识synchronized(下)的相关文章

内置锁(二)synchronized下的等待通知机制

一.等待/通知机制的简介 线程之间的协作: ??为了完成某个任务,线程之间需要进行协作,采取的方式:中断.互斥,以及互斥上面的线程的挂起.唤醒:如:生成者--消费者模式.或者某个动作完成,可以唤醒下一个线程.管道流已准备等等: 等待/通知机制: ? ?等待/通知机制 是线程之间的协作一种常用的方式之一,在显示锁Lock 和 内置锁synchronized都有对应的实现方式. 等待/通知机制 经典的使用方式,便是在生产者与消费者的模式中使用: 1.生产者负责生产商品,并送到仓库中存储: 2.消费者

深入浅出Java并发包—锁机制(一)

前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Java中的锁机制! Synchronized是基于JVM来保证数据同步的,而Lock则是在硬件层面,依赖特殊的CPU指令实现数据同步的,那究竟是如何来实现的呢?我们一一看来! 一.synchronized的实现方案 synchronized比较简单,语义也比较明确,尽管Lock推出后性能有较大提升,但是

Java并发知识整理

整理了一下前端时间学习Java并发的笔记,大约有40篇. 1. Java并发基础知识 并发基础(一) 线程介绍 并发基础(二) Thread类的API总结 并发基础(三) java线程优先级 并发基础(四) java中线程的状态 并发基础(五) 创建线程的四种方式 并发基础(六) 线程Thread类的start()和run() 并发基础(七) Thread 类的sleep().yeild().join() 并发基础(八) java线程的中断机制 并发基础(九) java线程的终止与中断 并发基础

高并发第三弹:线程安全-原子性

线程安全性? 感谢 [原子性]https://blog.csdn.net/fanrenxiang/article/details/80623884 线程安全性主要体现在三个方面:原子性.可见性.有序性 原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作 可见性:一个线程对主内存的修改可以及时的被其他线程观察到 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序. 本章主要关注一下原子性的方面 说到原子性,一共有两个方面需要学习一下,一个是JDK中

并发编程之多线程之间通讯

1.重排序概念 A.cpu会对代码执行顺序实现优化,不会对有依赖关系的数据做重排序.代码的执行顺序可能会发生改变,但是执行的结果不会发生任何改变. B.as-if-serial:不管怎么去做重排序,目的是提高并行度,但是不能影响到正常的结果. C.重排序在多线程情况下遇到 2.wait()和notify() A.wait()等待:让线程等待,并释放锁资源 B.noftify()唤醒:唤醒当前对象锁池被等待线程 C.注意:一定要在synchronized下使用,并且持有的是同一把锁. D.slee

Vector 是线程安全的,是不是在多线程下操作Vector就可以不用加Synchronized

如标题一样,如果之前让我回答,我会说,是的,在多线程的环境下操作Vector,不需要加Synchronized. 但是我今天无意间看到一篇文章,我才发现我之前的想法是错误的,这篇文章的地址: http://zhangbq168.blog.163.com/blog/static/2373530520082332459511/ 我摘抄关键的一部分: Vector 比 ArrayList慢,是因为vector本身是同步的,而arraylist不是所以,没有涉及到同步的推荐用arraylist. 看jd

Synchronized锁在Spring事务管理下,为啥还线程不安全?

大年初二,朋友问了我一个技术的问题(朋友实在是好学,佩服!) 开启10000个线程,每个线程给员工表的money字段[初始值是0]加1,没有使用悲观锁和乐观锁,但是在业务层方法上加了synchronized关键字,问题是代码执行完毕后数据库中的money 字段不是10000,而是小于10000 问题出在哪里? Service层代码:SQL代码(没有加悲观/乐观锁):用1000个线程跑代码:简单来说:多线程跑一个使用synchronized关键字修饰的方法,方法内操作的是数据库,按正常逻辑应该最终

震撼:多线程下的操作离不开synchronized

昨天在写一个聊天程序,在发送消息的时候是采用单独的一个线程,接收消息是在另一个线程中完成. 我在测试的过程中发现,有的时候当消息比较多时,比如: 当我刚刚发送完一条消息,这个时候要将我发送的消息添加到JTextPane元件中,刚好对方也发来一个消息,这个时间接收线程也将接收到的消息添加到了JTextPane元件,这就发生与线程“锁”有关的问题. 问题表现出的现象是,有的时候JTextPane无法正常显示消息内容,有的时候干脆就抛出元件内部的异常. 我当时也没想到是与线程有问,后来当我想到有可能与

jgs--多线程和synchronized

多线程 多线程是我们开发人员经常提到的一个名词.为什么会有多线程的概念呢?我们的电脑有可能会有多个cpu(或者CPU有多个内核)这就产生了多个线程.对于单个CPU来说,由于CPU运算很快,我们在电脑上运行多个软件时,每个软件在CPU上运行很短的时间就会切换成其他软件.由于来回切换的时间很短,我们感觉好像所有的程序都在同时运行,这也是多线程.多线程可以解决较多的用户访问同一个服务时压力过大的问题,可以更充分的利用计算机的性能. 多线程的问题 多线程的好处很多,可是相应的也出现了一些问题.其中最常见