Synchronized

在1.5以前是重量级锁,当遇到synchronzed时,其他线程需要等待并进入blocked状态。

在1.6以后,经过大量的测试发现加锁的地方经常被一个线程访问。所以对synchronized进行了锁的粒度优化,主要在对象头增加标记位因此有了偏向锁、轻量级锁、重量级锁。

三种常用用法Synchronized(A.class), synchronized(变量) synchronized method, 通过代码与bytecode对比发现,

  • synchronized method: 字节码中存在ACC_SYNCHRONIZED这个标记,当执行到这个行是,当前线程需要获取对象monitor锁,然后在进入方法处理业务,当业务正常退出方法或者异常都会自动释放monitor锁,这个是隐式运行的(更深入一点的这个ACC_SYNCHRONIZED在运行常量池中。method_info中的access_flags,但是在jdk8 bytecode没有发现这个标记。)

  • synchronized(): 在字节码中增加了monitorenter、monitorexit。

  • 锁升级

  1. 偏向锁:当线程获取到对象锁时(无竞争会将自己的线程id设置到对象头中),当再次获取时,查看对象头中记录的偏向锁的线程id是否一致,

如果一致则不需要cas设置。

如果不一致,因为锁是不能降级的,

所有这时会查看当前的线程id在栈帧中是否存活如果存活, 线程的栈帧已经不存在直接使用cas设置。

如果栈帧中存在对应的线程id,这是当前线程会撤销偏向锁,进入轻量级锁
  1. 轻量级->重量级锁:

当线程1已经成功设置偏向锁时,在DisplaceMarkword阶段(设置自己的线程id到对象头)

如果线程1成功设置,此时线程2cas设置时发现已经被设置,会撤销偏向锁(这个是有次数的当插销40次后会放弃偏向锁,升级进入到轻量级锁),进入自旋锁,(这里主要考虑用户态、内核态切换的时间消耗)故让线程2持有cpu进行自旋锁(-XX:PreBlockSpin一般是10可以设置具体查文档即可)。

线程2一直自旋(因此少量线程比较适合使用,如果n多个同时自旋,此时cpu在空转 空等,会出现cpu飙升,响应下降),

这时如果线程3进行cas设置,这是发现已经有线程2在竞争,则会升级锁到重量级锁(设置的也是对象头标记位)。使线程2、线程3进行阻塞状态。剩下的有操作系统调度管理了

  1. 话外篇

锁粗化:当锁的粒度变小会出现频繁的加锁、解锁,例如在一个方法内,没走一步都需要加锁、解锁,可以将各个步骤放在一次只加一次锁。减少锁动作

锁消除:JIT编译、逃逸分析等,将不可能存在共享资源竞争的锁去除了。

当线程持有锁后,如果这个线程内部执行动作比较耗时。这个程序时是没法被终止的,只能等自己退出、或者异常退出。后续的阻塞的线程才能重新获取锁。针对这种情况出现了Lock的概念。

Lock

锁可控、锁重入、公平、非公平

Lock在jdk底层开发了api,unsafe的方法,其中主要依赖顶层的AQS。线程对共享资源state变量进行cas设置,如果设置成功,设置线程持有锁,其他线程在设置失败后会有个短暂的自旋。然后会被LockSupport.park(this)【通过堆栈信息线程状态是WAITING(park, wait,sleep)】将当前线程挂起并加入到CLH队列中(是一个双向链表。尾插入,当前驱节点异常或者退出会自动替换链表的关系)。

  • ReentrantLock ArrayBlockingQueue内部定义了notFull,noEmpty,ReetrantLock,用来控制队列的put,take阻塞行为
    • Condition: 代替Object的wait、notify、notifyAll 为什么没有Object中的wait、notify。这个需要在synchronize()块中。不灵活
    • ArrayBlockingQueue: 数组阻塞队列
    • LinkedBlockingQueue: 阻塞连接队列
    • LinkedTransferQueue: 无界链表队列
  • AQS
  • CountDownLatch
  • Semaphore

参考

源码

package cn.linuxcrypt.api.demo;

public class SynchronizedTest {
    public synchronized void test1() {
        System.out.println("00");
    }

    public void test2() {
        synchronized (this) {
            System.out.println("bb");
        }
    }

    public void test3() {
        synchronized (SynchronizedTest.class) {
            System.out.println("cc");
        }
    }


    public static void main(String[] args) {
        SynchronizedTest t = new SynchronizedTest();


    }
}

Classfile .../target/classes/cn/linuxcrypt/api/demo/SynchronizedTest.class
  Last modified 2020-8-21; size 1062 bytes
  MD5 checksum 8c7b7c349fb0f4d9e7a7526673e8f33b
  Compiled from "SynchronizedTest.java"
public class cn.linuxcrypt.api.demo.SynchronizedTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #9.#32         // java/lang/Object."<init>":()V
   #2 = Fieldref           #33.#34        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #35            // 00
   #4 = Methodref          #36.#37        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #38            // bb
   #6 = Class              #39            // cn/linuxcrypt/api/demo/SynchronizedTest
   #7 = String             #40            // cc
   #8 = Methodref          #6.#32         // cn/linuxcrypt/api/demo/SynchronizedTest."<init>":()V
   #9 = Class              #41            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Lcn/linuxcrypt/api/demo/SynchronizedTest;
  #17 = Utf8               test1
  #18 = Utf8               test2
  #19 = Utf8               StackMapTable
  #20 = Class              #39            // cn/linuxcrypt/api/demo/SynchronizedTest
  #21 = Class              #41            // java/lang/Object
  #22 = Class              #42            // java/lang/Throwable
  #23 = Utf8               test3
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               t
  #29 = Utf8               MethodParameters
  #30 = Utf8               SourceFile
  #31 = Utf8               SynchronizedTest.java
  #32 = NameAndType        #10:#11        // "<init>":()V
  #33 = Class              #43            // java/lang/System
  #34 = NameAndType        #44:#45        // out:Ljava/io/PrintStream;
  #35 = Utf8               00
  #36 = Class              #46            // java/io/PrintStream
  #37 = NameAndType        #47:#48        // println:(Ljava/lang/String;)V
  #38 = Utf8               bb
  #39 = Utf8               cn/linuxcrypt/api/demo/SynchronizedTest
  #40 = Utf8               cc
  #41 = Utf8               java/lang/Object
  #42 = Utf8               java/lang/Throwable
  #43 = Utf8               java/lang/System
  #44 = Utf8               out
  #45 = Utf8               Ljava/io/PrintStream;
  #46 = Utf8               java/io/PrintStream
  #47 = Utf8               println
  #48 = Utf8               (Ljava/lang/String;)V
{
  public cn.linuxcrypt.api.demo.SynchronizedTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;

  public synchronized void test1();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String 00
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;

  public void test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #5                  // String bb
         9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 9: 0
        line 10: 4
        line 11: 12
        line 12: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 17
          locals = [ class cn/linuxcrypt/api/demo/SynchronizedTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void test3();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #6                  // class cn/linuxcrypt/api/demo/SynchronizedTest
         2: dup
         3: astore_1
         4: monitorenter
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #7                  // String cc
        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit
        15: goto          23
        18: astore_2
        19: aload_1
        20: monitorexit
        21: aload_2
        22: athrow
        23: return
      Exception table:
         from    to  target type
             5    15    18   any
            18    21    18   any
      LineNumberTable:
        line 15: 0
        line 16: 5
        line 17: 13
        line 18: 23
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class cn/linuxcrypt/api/demo/SynchronizedTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #6                  // class cn/linuxcrypt/api/demo/SynchronizedTest
         3: dup
         4: invokespecial #8                  // Method "<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 22: 0
        line 25: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
            8       1     1     t   Lcn/linuxcrypt/api/demo/SynchronizedTest;
    MethodParameters:
      Name                           Flags
      args
}
SourceFile: "SynchronizedTest.java"