程序员Java 并发

【Java 并发笔记】syschronized 相关整理

2018-12-10  本文已影响19人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

1. synchronized 关键字

1.1 双重检查锁实现单例

class LockSingleton{
    private volatile static LockSingleton singleton;
    private LockSingleton(){}
    public static LockSingleton getInstance(){
        if(singleton==null){
            synchronized(LockSingleton.class){
                if(singleton==null){
                    singleton=new LockSingleton();
                }
            }
        }
        return singleton;
    }
}

1.2 枚举实现单例

public class MyEnum {
    public static MyEnum NumberZero;
    public static MyEnum NumberOne;
    public static MyEnum NumberTwo;
    public static MyEnum NumberThree;

    static {
        NumberZero = new MyEnum(0);
        NumberOne = new MyEnum(1);
        NumberTwo = new MyEnum(2);
        NumberThree = new MyEnum(3);
    }

    private final int value;

    private MyEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
public enum MyEnum {
    NumberZero(0),
    NumberOne(1),
    NumberTwo(2),
    NumberThree(3);

    private final int value;

    MyEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
public enum MyEnum {
    NumberZero,
    NumberOne,
    NumberTwo,
    NumberThree;

    public int getValue() {
        return ordinal();
    }
}
enum EnumSingleton{
    INSTANCE;
    public void doSomeThing(){
    }
}

1.3 synchronized 的实现原理

1.3.1 Java 对象头

对象头信息

1.3.2 Monitor(管程或监视器锁)

临界区

Monitor 对象 / 锁 / 条件变量等

ObjectMonitor

1.3.2.1 Monitor 与 Java对象及线程的关联

1.3.2.2 ObjectMonitor(内置锁) 的具体实现

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //记录个数
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
}
ObjectMonitor 方法 说明
enter方法 获取锁。
exit 方法 释放锁。
wait 方法 为 Java 的 Object 的 wait 方法提供支持。
notify 方法 为 Java 的 Object 的 notify 方法提供支持。
notifyAll 方法 为 Java 的 Object 的 notifyAll 方法提供支持。

显式同步

public class Test {
    public static void main(String[] args) {
        synchronized (Test.class) {
        }
    }
}
......
public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc_w         #2                  
         3: dup           
         4: astore_1      
         5: monitorenter     // 同步模块开始   
         6: aload_1       
         7: monitorexit        // 同步模块结束
         8: goto          16
        11: astore_2      
        12: aload_1       
......

隐式同步

public class Test {
    public static void main(String[] args) {
        test();
    }

    public synchronized static void test() {
    }
}
......
public static synchronized void test();
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED     // 检查访问标志
    Code:
      stack=0, locals=0, args_size=0
         0: return        
      LineNumberTable:
        line 13: 0
......

1.3.3 类型指针

句柄访问方式

句柄访问方式

直接访问方式

直接访问方式

1.3.4 synchronized 的语义

1.4 synchronized 的优化

1.4.1 CAS

/**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

1.4.1.1 CAS 存在的问题

1.4.2 偏向锁

1.4.2.1 偏向锁的获取

偏向锁的获取

1.4.2.2 偏向锁的释放

偏向锁的释放

1.4.2.3 安全点停顿日志

         vmop                    [threads: total initially_running wait_to_block]    [time: spin block sync cleanup vmop] page_trap_count
0.036: EnableBiasedLocking              [       7          0              1    ]      [     0     0     0     0     0    ]  0   
Total time for which application threads were stopped: 0.0000860 seconds, Stopping threads took: 0.0000180 seconds
         vmop                    [threads: total initially_running wait_to_block]    [time: spin block sync cleanup vmop] page_trap_count
0.071: RevokeBias                       [       9          0              1    ]      [     0     0     0     0     0    ]  0   
Total time for which application threads were stopped: 0.0000810 seconds, Stopping threads took: 0.0000220 seconds
         vmop                    [threads: total initially_running wait_to_block]    [time: spin block sync cleanup vmop] page_trap_count
0.071: RevokeBias                       [       9          0              1    ]      [     0     0     0     0     0    ]  0   
Total time for which application threads were stopped: 0.0001330 seconds, Stopping threads took: 0.0001090 seconds
         vmop                    [threads: total initially_running wait_to_block]    [time: spin block sync cleanup vmop] page_trap_count
0.071: no vm operation                  [       7          1              1    ]      [     0     0     0     0    10    ]  0   
参数 说明
vmop Java 虚拟机操作类型(时间戳:操作类型)。
threads 线程概况(安全点里的总线程数(total) ;安全点开始时正在运行状态的线程数(initially_running) ;在 Java 虚拟机操作开始前需要等待其暂停的线程数(wait_to_block))。
time 执行操作时间(等待线程响应 safepoint 号召的时间(spin);暂停所有线程所用的时间(block);等于 spin + block,这是从开始到进入安全点所耗的时间,可用于判断进入安全点耗时(sync);清理所用时间(cleanup);真正执行 Java 虚拟机操作的时间(vmop))。

1.4.2.4 偏向锁小结

1.4.3 轻量级锁

1.4.3.1 轻量级锁的加锁过程

轻量级锁的获取 图 A 线程堆栈与对象头的状态

1.4.3.2 轻量级锁的释放

从释放锁的线程(已经获得轻量级锁的线程)的角度理解

轻量级锁的释放 轻量级锁的膨胀

从尝试获取锁线程的角度理解

1.4.3.3 轻量级锁小结

1.4.4 重量级锁

1.4.4.1 重量级锁的获取

重量级锁的获取

1.4.4.2 重量级锁的释放

重量级锁的释放

1.4.4.3 各种锁比较

优点 缺点 适用场景
偏向锁 加锁解锁不需要额外消耗 如果线程间存在竞争,会带来额外的锁撤销消耗 一个线程访问同步块的场景
轻量级锁 竞争线程不会阻塞,提高程序响应速度 得不到锁竞争的线程,会自旋消耗 CPU 追求响应时间,同步块执行速度很快的场景
重量级锁 线程竞争不适用自旋,不消耗 CPU 线程阻塞,响应速度慢 追求吞吐量,同步块执行时间较长的场景

1.4.5 自旋锁

1.4.5.1 自旋锁的优缺点

1.4.5.2 自旋锁时间阈值

1.4.6 锁的优化

减少锁的时间

减少锁的粒度

锁粗化

使用读写锁

读写分离

使用 CAS

消除缓存行的伪共享

消除锁

1.5 synchronized 的关键点

synchronized 的可重入性

中断与 synchronized

等待唤醒机制与 synchronized

参考

https://www.linuxidc.com/Linux/2018-02/150798.htm
https://blog.csdn.net/javazejian/article/details/72828483
https://www.jianshu.com/p/d53bf830fa09

上一篇 下一篇

猜你喜欢

热点阅读