synchronized 既保证原子性,又保证内存可见性,是一种线程同步的方式,是锁机制的一种 java 实现。synchronized 的实现基于 JVM 底层,JVM 是基于 monitor 实现的,而 monitor 的实现依赖于操作系统的互斥实现。
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
- }
将这段代码通过
反编译一下,重点关注一下编译后的第 3 行和第 13 行。
- javap -c
- 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
我是葛一凡,希望对你有用。
来源: