彻底了解synchornized关键字

379 阅读4分钟

1: 8种锁类型

1.1: 标准访问ab二个线程,是先打印t1还是t2 ???

public class SyncUnit {
    
    public synchronized void t1() {
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();

        Thread.sleep(100);

        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.2: t1方法暂停3秒钟,是先打印t1还是t2 ???

public class SyncUnit {

    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();

        Thread.sleep(100);

        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }

1.3: 新增一个普通方法hello(),是先打印t1还是hello ????

public class SyncUnit {

    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public void hello() {
        System.out.println("hello");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();

        Thread.sleep(100);

        new Thread(() -> {
           syncUnit.hello();
        }).start();
    }
}

1.4: 现在有二个SyncUnit对象,是先打印t1还是t2 ???

public class SyncUnit {

    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }

1.5: 二个静态同步方法,一个SuncUnit对象,是先打印t1还是t2 ????

public class SyncUnit {

    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public static synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.6: 二个静态同步方法,二个SyncUnit对象,是先打印t1还是t2

public class SyncUnit {

    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public static synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }
}

1.7: 一个静态同步方法,普通同步方法,一个SyncUnit对象,是先打印t1还是t2

public class SyncUnit {

    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.8 一个静态同步方法,普通同步方法,二个SyncUnit对象,是先打印t1还是t2

public class SyncUnit {

    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }

    public synchronized void t2() {
        System.out.println("t2");
    }

    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }
}

1.9 : 结论

  • 1: sync修饰普通方法,那么锁的当前对象,同一个实例是可以有多个对象的,一个对象是一把锁
  • 2: sync修改静态方法,那么锁的是当前实例,实例在当前上下文中只有一个
  • 3: 针对后面几种情况希望大家好好思考,有什么问题可以评论联系我

2:sync是的底层实现原理

2.1: 同步代码块

public class SyncUnit {

  private Object obj = new Object();

  public void t1() {
      synchronized (obj) {
          System.out.println("123");
      }
  }
}

使用 javap -c SyncUnit.class 查看字节码,底层就是使用的是monitorentermonitorexit

image.png

2.2: 一个monitorenter 一定对应多个 monitorexit 吗??

public class SyncUnit {

   private Object obj = new Object();

   public void t1() {
       synchronized (obj) {
           System.out.println("123");
           throw new RuntimeException("123");
       }
   }
}

查看字节码文件,你会发现只有一个monitorexit

image.png

2.3 同步方法

public class SyncUnit {

  private Object obj = new Object();

  public synchronized void t1() {
      System.out.println("123");
  }
}

查看字节码,同步方法使用的是 ACC_SYNCHRONIZED来修饰的,同步方法也是一样

image.png

4: sync锁升级

4.1: 无锁

  • 无锁代表着根本就不存在线程之间竞争,方法不需要添加做任何同步

4.2: 偏向锁

  • HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。偏向锁就是在这种情况下出现的,它的出现只是为了解决只有在一个线程执行同步时提高性能

4.3: 轻量级锁

  • 当存在锁竞争的时候,会通过自旋锁的方式来获取轻量级锁,也就是通过CAS来获取锁

5: 锁消除与锁粗化

5.1: 锁消除: 每次执行t1方法都会创建一个Object对象,我们知道一个对象是一把锁,所以有n个线程同时执行t1方法就会创建n个对象,也就是有n把锁,那么只时候加不加synchronized根本没有区别,这就是锁消除

public class SyncUnit {
    
   public  void t1() {
       Object obj = new Object();
       synchronized (obj){
           System.out.println("123");
       }
   }
   
}

5.2: 锁粗化,虽然这里加了4个synchornized ,但是其实可以只用一个就可以了,这一系列的连续操作都会对同一个对象反复加锁及解锁。即使没有出现线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗

public class SyncUnit {

   private Object obj = new Object();
   public  void t1() {
       synchronized (obj){
           System.out.println("1");
       }
       synchronized (obj){
           System.out.println("2");
       }
       synchronized (obj){
           System.out.println("3");
       }
       synchronized (obj){
           System.out.println("4");
       }
   }

       //锁粗化
       public  void t1() {
       synchronized (obj){
           System.out.println("1");
           System.out.println("2");
           System.out.println("3");
           System.out.println("4");
       }
   
   }