synchronized

为什么用?怎么用?底层是怎么实现的?能否写一段代码展示死锁?顺便讲一下DCL单例模式咯?jdk做过哪些优化?

  • 为什么用?
    • 多线程环境为了保障共享资源访问的正确性,引入锁来确保一个时间点只有一个线程能执行被锁住的代码
  • 怎么用?
    • 修饰代码块
      • // 类锁
          private void incr() {
                synchronized (this) {
                    count++;
                }
            }

        // 类锁
          private void incr() {
                synchronized (Count.class) {
                    count++;
                }
            }
         
    • 修饰方法      
      • // 对象锁  
        private synchronized void incr() { count++; }

        // 类锁 -

        private static synchronized void incr() {
                count++;
          }
         

           

  • 底层实现
    • 同步代码块时 
      • monitorenter 累加实现可重入
      • monitorexit 为什么有2个 - 异常退出  
    • 同步方法时是
      • 在常量池中依赖 ACC_SYNCHRONIZED 标识符 - 也是通过monitor监视器实现
  • 死锁代码
    • 
      
      static Object a = new Object();
      static Object b = new Object();
      public static void main(String[] args) {
              new Thread(() -> {
                  synchronized (a) {
                      System.out.println("t1 get lock a");
                      try {
                          Thread.sleep(100);
                          System.out.println("t1 wakes");
                          synchronized (b) {
                              System.out.println("t1 get lock b");
                          }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }).start();
      
              new Thread(() -> {
                  synchronized (b) {
                      System.out.println("t2 get lock b");
                      try {
                          Thread.sleep(100);
                          System.out.println("t2 wakes");
                          synchronized (a) {
                              System.out.println("t2 get lock a");
                          }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }).start();
          }

      // 线程1抱着锁a睡着了,睡醒后要拿锁b 。 线程2抱着锁b睡着,睡醒后要拿锁a - 陷入相互等待
  • DCL单例模式 - 空指针
    • public class SingletonPattern {
      
          private volatile static SingletonPattern singletonPattern = null;
          
          public static SingletonPattern getDCLSingleton() {
              if (singletonPattern == null) {
                  synchronized (SingletonPattern.class) {
                      if (singletonPattern == null) {
                          singletonPattern = new SingletonPattern();
                      }
                  }
              }
              return singletonPattern;
          }
          
          public static void main(String[] args) {
              System.out.println(getDCLSingleton());
          }
      }

      // 懒汉式单例 singletonPattern = new SingletonPattern(); 分解为三步 1.分配内存空间 2.初始化对象 3.将实例指向分配的地址 重排可能会执行 1-3-2 会出现空指针异常
      // volatile 使用内存屏障禁止指令重排
      
      

       

  • 锁的优化(jdk1.6)  为了避免使用重量级锁 - 锁是否可以降级由实现的jvm决定 https://www.cnblogs.com/xdyixia/p/9364247.html
    1. 偏向锁 - 减少轻量级锁自旋
      • 持有偏向锁的线程不会自动释放,如果有线程竞争,才会释放锁
      • 若竞争失败,偏向锁升级为轻量级锁。原来持有偏向锁的线程会释放锁(会导致stw操作)。
    2. 轻量级锁 - 减少重量级锁互斥
      1. 获取锁
        1. 拷贝Mark Word信息到自己线程的栈帧
        2. cas操作将锁对象Mark Word指向当前线程栈帧
          1. CAS若失败会判断锁对象Mark Word是否指向当前线程栈帧,若不是,则膨胀锁.Mark Word里面的指针就指向重量级锁
      2. 和释放锁都是通过CAS实现
        1. 持有轻量级锁的线程释放锁时会取出自己栈帧中之前复制的Mark Word数据 CAS替换锁对象的Mark Word。如果成功就释放成功
        2. 失败的话,说明Mark Word已经被改成重量级锁指针。已经有线程在阻塞了。在释放时要唤醒阻塞线程
    3. 自旋锁 - 还是为了避免线程直接使用重量级锁
      1. 执行空循环等待 - 当然如果等待锁的时间过长,空循环也占用cpu
      2. 适应性自旋 - 
        1. 对于同一个锁对象,如果上一次自旋获取到锁,且该锁被线程持有,那么其他获取锁的线程允许自旋更多的次数以获得该锁。
        2. 相反如果某个锁很少被自旋获取到,那么以后获取该锁时,自旋的次数将会变少,甚至可能省略自旋过程。
    4. 重量级锁
      1. 实现重量级锁,需要依赖操作系统切换用户态到内核态。切换成本高,消耗性能严重
      2. 实现 队列 - https://blog.csdn.net/kirito_j/article/details/79201213
    5. 锁升级过程
      1.   

         

         

 

 

参考文章

  1. synchronized的实现原理及锁优化
  2. JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比
  3. 锁升级 https://www.cnblogs.com/kancy/p/10482756.html

 

  

posted @ 2020-01-02 13:38  渠成  阅读(95)  评论(0)    收藏  举报