八锁现象(彻底理解什么是锁)

本文主要介绍常见面试题,JUC并发编程中的八锁现象。通过这篇文章,可以让你彻底搞清楚锁对象或者是锁方法的不同之处!

问题一:标准情况下,一个对象,两个同步方法先执行哪一个?

1、测试代码:

public class Test01 {
    public static void main(String[] args) {
        //创建资源对象
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone.call();
        }, "B").start();

    }

}

//资源类
class Phone {

    public synchronized void sendSms() {
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}

控制台打印结果:
先打印发短信,等待一秒后打印打电话

2、总结
Synchronized锁,锁的是对象的调用者,因为这两个线程同时用的一个对象phone,那么谁先拿到这把锁,谁就执行对应的方法

问题二:给其中一个同步方法加一个2秒的延时,先执行哪一个方法?

1、测试代码

public class Test01 {
    public static void main(String[] args) {
        //创建资源对象
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //模拟延时
/*        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        new Thread(() -> {
            phone.call();
        }, "B").start();

    }
}

//资源类
class Phone {

    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

}


控制台打印结果:
延迟2秒后,打印顺序为发短信  打电话

2、总结
Synchronized锁,锁的是对象的调用者,因为这两个线程同时用的一个对象phone,那么谁先拿到这把锁,谁就执行对应的方法

问题三:资源类中增加普通方法,先执行加锁的还是普通的方法?

1、测试代码

public class Test02 {
    public static void main(String[] args) {
        //创建资源对象
        Phone2 phone = new Phone2();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone.hello();
        }, "B").start();

    }

}

//资源类
class Phone2 {

    public synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

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


控制台打印结果:

先打印hello,过了4秒钟打印发短信

2、总结
普通方法没有加Synchronized锁,那么他就不是同步方法,不受锁的影响

问题四:创建两个对象,都执行同步方法,先执行哪一个?

1、测试代码

public class Test02 {
    public static void main(String[] args) {
        //创建资源对象,创建两个对象
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.call();
        }, "B").start();

    }

}

//资源类
class Phone2 {

    public synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

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

控制台打印结果:
先打印打电话,过了4秒钟打印发短信

2、总结

Synchronized锁的是对象的调用者,因为我们创建了两个对象,那么就会有两把锁,锁不一样,就按照他们各自的延迟时间执行,互不影响

问题五:增加两个静态的同步方法,两个线程调用,先执行哪一个?

1、测试代码

public class Test03 {
    public static void main(String[] args) {
        //创建资源对象
        Phone3 phone = new Phone3();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();

        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone.call();
        }, "B").start();

    }
}

//资源类
class Phone3 {
    public static synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }
}


控制台打印结果:
等待4秒钟打印发短信,紧接着执行打电话

2、总结

和问题一对比学习!
其一是Synchronized锁的对象是方法的调用者,因为只创建了一个对象,那么谁先拿到锁,谁先执行
其二是因为加了Static关键字,该方法就变成了静态方法,随着类的加载而加载,那么这个测试中的锁锁的是Class这个模板,Class模板全局唯一,也就是说他们还是共用一把锁,谁先拿到锁,谁先执行方法


问题六:创建两个对象,调用两个静态同步方法,先执行哪一个?

1、测试代码

public class Test03 {
    public static void main(String[] args) {
        //创建资源对象
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();

        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.call();
        }, "B").start();

    }
}

//资源类
class Phone3 {
    public static synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }
}

控制台打印结果:
和问题四执行结果相同,等待4秒钟打印发短信,紧接着执行打电话

2、总结
出现和问题四相同的执行结果的原因是Synchronized锁的是两个静态的方法,静态方法随着类的加载而加载,尽管我们创建了两个对象,但是他们的Class模板全局唯一,Synchronized锁锁的是这个唯一的Class模板,因此他们还是共用一把锁,所以还是先执行先得到锁的发短信方法,尽管他有4秒的延时。

问题七:一个静态同步方法,一个普通同步方法,一个对象,先执行哪个方法呢?

1、测试代码

public class Test04 {
    public static void main(String[] args) {
        //创建资源对象
        Phone4 phone = new Phone4();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();

        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone.call();
        }, "B").start();

    }
}

//资源类
class Phone4 {
    //静态同步方法
    public static synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通同步方法
    public  synchronized void call() {
        System.out.println("打电话");
    }
}

控制台打印结果:
先打印打电话,等待4秒后执行发短信

2、总结
静态同步方法锁的是Class模板,而普通同步方法锁的是对象的调用者,本质上是两把锁,执行的时候互不影响,按照他们本身的延迟时间进行先后的执行

问题八:两个对象,一个静态同步方法,一个普通同步方法,先执行哪一个?

1、测试代码

public class Test04 {
    public static void main(String[] args) {
        //创建资源对象
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();

        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.call();
        }, "B").start();

    }
}

//资源类
class Phone4 {
    //静态同步方法
    public static synchronized void sendSms() {
        //模拟延时
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通同步方法
    public  synchronized void call() {
        System.out.println("打电话");
    }
}


控制台打印结果:
先打印打电话,等待4秒后执行发短信方法

2、总结
还是同样的道理,静态同步方法锁的是Class模板,而普通同步方法锁的是对象的调用者,本质上还是两把锁,尽管我们创建了两个对象,但是依旧不改变两把锁的根本,所以执行结果是先打印打电话,等待4秒后执行发短信方法。

总结

主要就是搞清楚锁的对象是什么,new出来的是具体的对象,可以不唯一,但是通过static修饰的同步方法,是锁的全局唯一的Class模板

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Be explorer

若认可笔者文章,手头富裕望支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值