一、八锁现象:通过八个例子理解synchronized锁的对象到底是谁。
二、总结
我们先看总结。只要理解了synchronized锁的对象到底是谁,就能理解线程执行顺序:如果锁的对象是同一个,那么一个方法执行前需要等待另一个方法释放锁,如果锁的对象不是同一个,那么两个方法执行不需要等待彼此释放锁。
- 当synchronized修饰实例方法,锁的对象是方法的实例。
- 这种情况下创建出了一个实例,锁的是一个实例,用这个对象调用两个同步实例方法,它们共用的是一把锁。
- 创建出两个实例,锁的对象是两个实例,分别调用两个同步方法,它们用的是两个不同的锁。
- 当synchronized修饰静态方法,锁的对象是当前类。会作用于类的所有对象实例 。
- 所以这种情况下不论创建几个对象,锁的对象都是同一个Class。
- 一个类里同时存在synchronized修饰的实例方法和静态方法,锁的对象是同一个吗?
- 显然,从1、2可知实例同步方法是有根据实例创建锁的,有几个实例就有几个锁,静态同步方法是根据类创建锁的,一个类只有一把锁,因此实例方法和静态方法锁的是不同对象。
- 没有synchronized修饰的方法是普通方法,不受锁的影响。
三、八锁现象
接下来我们通过八个例子深入理解上述总结。假设有一个Phone对象,存在sendMeg()和call()两个方法,分别用两个线程A、B去调用它们。
- 标准情况下,执行结果为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");}
}
}
- 令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");}
}
- 增加一个普通方法,先执行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");
}
}
- 创建两个对象,先执行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");}
}
- 两个都是静态方法。先执行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");}
}
- 两个都是静态方法,且创建了两个对象。先执行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");}
}
- 一个静态同步方法,一个普通同步方法,锁的对象不是同一个。先执行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");}
}
- 创建两个对象,调用静态同步方法和普通同步方法。先执行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");}
}