synchronized锁的理解

79 阅读3分钟

前言

synchronized是阻塞锁,非公平锁。

java中一个对象包含:

  1. 对象头:Mark Word 和 Class Pointer Address(对应类信息的klass信息)
  2. 对象中的实际数据:instance data
  3. 对齐填充: padding

锁的状态:

  1. 无锁状态
  2. 偏向锁状态
  3. 轻量级锁状态
  4. 重量级锁状态

对于一个对象内存中的具体数据分配,添加工具jol使用ClassLayout打印内存信息。

    implementation 'org.openjdk.jol:jol-core:0.14' 
public static String test(){
    Test test = new Test();
    return ClassLayout.parseInstance(test).toPrintable();
}
输出:
Test:com.georege.mylearn.Test object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           4e 36 03 20 (01001110 00110110 00000011 00100000) (537081422)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

当第一个线程调用synchronized这个对象的时候,对象状态变成偏向锁,带上当前线程的id,又来一个线程,会变成轻量级锁,带上指向栈中锁积累(Lock Record)的指针,又来一个线程,变成重量级锁,此时指向互斥量(Monitor)的指针。

synchronized代码块都有一个monitorenter和monitorexit, monitor-enter v1和monitor-exit v1

synchronized关键字依托于对象头的markWord中锁的状态信息,实现线程的并发同步操作,有4种锁的状态,synchronized再最新java中已经有相当好的性能。在属于轻量级锁的时候,等待线程会自选等待,当有第3个线程进来,或者自旋一定数量(根据jvm的优化判断是需要自旋转或直接升级重量级锁),就会升级为重量级锁,重量级锁通过Monitor变量来维护等待线程队列。当等待线程等待一定时间或者own_thread退出解锁的时候,等待队列被唤醒,进入唤醒队列,唤醒队列中的各个线程通过竞争获取锁,获得成功就变成own。不成功继续进入等待队列,等待下一次唤醒。所以synchronized最终效果是阻塞和非公平锁。

synchronized关键字字节码指令是MonitorEnter,MonitorExt。当一个对象没有锁的时候,synchronized触发获取锁,会将对象头的markWord中的信息锁状态变成偏向锁,又来一个线程获取锁,该对象会升级为轻量级锁,这个线程会自旋等待,直到获取锁,又来一个线程或者自旋一定数量以后,该对象会升级为重量级锁状态,重量级锁状态指向一个Monitor对象指针,Monitor维护线程等待队列,处于等待队列的线程也处于阻塞状态,当等待线程等待一定时间或者own_thread退出解锁的时候,等待队列被唤醒,进入唤醒队列,唤醒队列中的各个线程通过竞争获得锁,获得成功就变成own。不成功继续进入等待队列继续Blocked,等待下一次唤醒。synchronized是阻塞锁和非公平锁。

一个对象没有锁->synchronized触发获取锁->对象头变成偏向锁->又来一个线程2->轻量级锁->自旋等待->又来一个线程3或自旋一定数量后->重量级锁。

synchronized可能导致的场景问题:

  1. 阻塞主线程。synchronized异步操作数据集合。
  2. synchronized都是锁的对象klass
  3. 如果是普通类,锁的对象klass会有隐患,使用普通类的成员变量obj锁即可,除非是单例可以使用。
  4. 使用synchronized(this)和非静态方法synchronized是一样的,都是同一个对象
  5. 尽量不适用嵌套synchronized,可能触发死锁。