synchronized:锁的对象到底是谁?快速理解八锁现象

一、八锁现象:通过八个例子理解synchronized锁的对象到底是谁。

二、总结

我们先看总结。只要理解了synchronized锁的对象到底是谁,就能理解线程执行顺序:如果锁的对象是同一个,那么一个方法执行前需要等待另一个方法释放锁,如果锁的对象不是同一个,那么两个方法执行不需要等待彼此释放锁。

  1. 当synchronized修饰实例方法,锁的对象是方法的实例。
    • 这种情况下创建出了一个实例,锁的是一个实例,用这个对象调用两个同步实例方法,它们共用的是一把锁。
    • 创建出两个实例,锁的对象是两个实例,分别调用两个同步方法,它们用的是两个不同的锁。
  2. 当synchronized修饰静态方法,锁的对象是当前类。会作用于类的所有对象实例 。
    • 所以这种情况下不论创建几个对象,锁的对象都是同一个Class。
  3. 一个类里同时存在synchronized修饰的实例方法和静态方法,锁的对象是同一个吗?
    • 显然,从1、2可知实例同步方法是有根据实例创建锁的,有几个实例就有几个锁,静态同步方法是根据类创建锁的,一个类只有一把锁,因此实例方法和静态方法锁的是不同对象。
  4. 没有synchronized修饰的方法是普通方法,不受锁的影响。

三、八锁现象

接下来我们通过八个例子深入理解上述总结。假设有一个Phone对象,存在sendMeg()和call()两个方法,分别用两个线程A、B去调用它们。

  1. 标准情况下,执行结果为sendMeg、call,因为锁的对象是方法的调用者,sendMeg先拿到锁。
    public class Test {
	    public static void main(String[] args) throws InterruptedException {
	        Phone phone = new Phone();
	        new Thread(()->phone.sendMeg(),"A").start();
	        TimeUnit.SECONDS.sleep(1);
	        new Thread(()->phone.call(),"B").start();
	    }
	 }
	class Phone{
	    // 锁的对象是方法的调用者
	    public synchronized void sendMeg(){System.out.println("sendMeg");}
	    public synchronized void call(){ System.out.println("call");}
    }
}
  1. 令sendMeg延迟4s,执行结果为sendMeg、call,因为锁的对象是方法的调用者(phone),两个方法用的是同一把锁,sendMsg先拿到锁。

  public class Test {
  
      public static void main(String[] args) throws InterruptedException {
          Phone phone = new Phone();
          new Thread(()->phone.sendMeg(),"A").start();
          TimeUnit.SECONDS.sleep(1);
          new Thread(()->phone.call(),"B").start();
      }
  }
  class Phone{
      // 锁的对象是方法的调用者
      public synchronized void sendMeg(){
      	TimeUnit.SECONDS.sleep(4);
      	System.out.println("sendMeg");
      }
      public synchronized void call(){ System.out.println("call");}
  }
  1. 增加一个普通方法,先执行sendMsg还是hello?先执行hello。因为普通方法没有加锁,当sendMeg延迟4s时,普通方法不用等待锁,直接执行。
  public class Test {
  
      public static void main(String[] args) throws InterruptedException {
          Phone phone = new Phone();
          new Thread(()->phone.sendMeg(),"A").start();
          TimeUnit.SECONDS.sleep(1);
          new Thread(()->phone.hello(),"B").start();
      }
  }
  class Phone{
      // 锁的对象是方法的调用者
      public synchronized void sendMeg(){
      	TimeUnit.SECONDS.sleep(4);
      	System.out.println("sendMeg");
      }
      public synchronized void call(){ System.out.println("call");}
      public void hello(){
  		System.out.println("hello");
  	}
  }
  1. 创建两个对象,先执行sendMeg还是call?先执行call。因为锁的对象不同(phone1,phone2),有两把锁,call不必等待sendMeg释放锁。
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->phone1.sendMeg(),"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->phone2.call(),"B").start();
    }
}
class Phone{
    // 锁的对象是方法的调用者
    public synchronized void sendMeg(){
    	TimeUnit.SECONDS.sleep(4);
    	System.out.println("sendMeg");
    }
    public synchronized void call(){ System.out.println("call");}
}
  1. 两个都是静态方法。先执行sendMeg。因为用的是同一把Class锁,需要等待对方释放锁。
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->phone.sendMeg(),"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->phone.call(),"B").start();
    }
}
class Phone{
    // 静态
    public static synchronized void sendMeg(){
    	TimeUnit.SECONDS.sleep(4);
    	System.out.println("sendMeg");
    }
    // 静态
    public static synchronized void call(){ System.out.println("call");}
}
  1. 两个都是静态方法,且创建了两个对象。先执行sendMeg。即使创建了两个对象,用的还是同一把Class锁。
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->phone1.sendMeg(),"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->phone2.call(),"B").start();
    }
}
class Phone{
    // 静态
    public static synchronized void sendMeg(){
    	TimeUnit.SECONDS.sleep(4);
    	System.out.println("sendMeg");
    }
    // 静态
    public static synchronized void call(){ System.out.println("call");}
}
  1. 一个静态同步方法,一个普通同步方法,锁的对象不是同一个。先执行call。
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->phone.sendMeg(),"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->phone.call(),"B").start();
    }
}
class Phone{
    // 静态同步方法
    public static synchronized void sendMeg(){
    	TimeUnit.SECONDS.sleep(4);
    	System.out.println("sendMeg");
    }
    // 普通同步方法
    public synchronized void call(){ System.out.println("call");}
}
  1. 创建两个对象,调用静态同步方法和普通同步方法。先执行call。显然,创建两个对象用的还是两个不同锁(一个Class锁一个实例锁),call方法不必等待sendMeg释放锁。
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->phone1.sendMeg(),"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->phone2.call(),"B").start();
    }
}
class Phone{
    // 静态同步方法
    public static synchronized void sendMeg(){
    	TimeUnit.SECONDS.sleep(4);
    	System.out.println("sendMeg");
    }
    // 普通同步方法
    public synchronized void call(){ System.out.println("call");}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值