• 4、8锁现象


    8锁现象

    • 8锁现象,实际对应的就是8个问题

    • 掌握了这8个问题后:可以清楚判断锁的是谁!永远的知道什么是锁,锁到底锁的是谁!

    锁方法的调用者

    问题一

    • 在标准情况下,两个线程先打印 发短信 还是 打电话 ?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test1 {
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            // 线程A
            new Thread(()->{
                phone.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B
            new Thread(()->{
                phone.call();
            }, "B").start();
        }
    }
    class Phone{
    
        public synchronized void seedMsg(){
            System.out.println("发短信");
        }
    
        public synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    为什么?

    如果你只是认为线程A先被调用的,那是不对的。现在抱着这个想法,再去思考下面的问题

    问题二

    • 在发短信方法中,延迟4秒,两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test1 {
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            // 线程A,人
            new Thread(()->{
                phone.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone.call();
            }, "B").start();
        }
    }
    // 手机
    class Phone{
    
        public synchronized void seedMsg(){
            // 4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
    
        public synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    答案还是发短信。

    为什么?

    • 首先知道 锁的对象是谁?因为 synchronized 加在方法上,所以锁的对象是 方法的调用者

    • 所以两个方法用的是同一个锁,谁先拿到谁先执行!

    通俗解释:

    • phone对象,就是方法的调用者,也就是手机,它可以打电话和发短信。

    • 现在有两个人线程A 和 线程B,他们一个想打电话,一个想发短信。

    • 线程A,先拿到锁(也就是手机),抱着锁(手机)睡了4秒。

    • 线程B肯定拿不到锁(手机),需要等待。

    问题三

    • Phone类增加一个普通方法,线程B调用,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test2 {
        public static void main(String[] args) {
            Phone2 phone = new Phone2();
    
            // 线程A,人
            new Thread(()->{
                phone.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone.hello();
                
            }, "B").start();
        }
    }
    
    // 手机
    class Phone2{
        //同步方法
        public synchronized void seedMsg(){
            // 4秒延迟
            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()方法,是普通方法,根本不受锁的影响。

    通俗解释:

    • 锁的是方法的调用者(手机)

    • 现在 线程B 调用普通方法,相当于可以远程操控,接电话一样,不需要接收消息

    问题四

    • 创建两个phone对象,线程调用不同对象的方法,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test2 {
        public static void main(String[] args) {
            Phone2 phone1 = new Phone2();
            Phone2 phone2 = new Phone2();
    
            // 线程A,人
            new Thread(()->{
                phone1.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone2.call();
            }, "B").start();
        }
    }
    
    // 手机
    class Phone2{
        //同步方法
        public synchronized void seedMsg(){
            // 4秒延迟
            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");
        }
    
    }

    答案是打电话。

    为什么?

    最重要的一点,synchronized用在方法上,那么锁的是方法的调用者。现在有两个调用者,互不影响。

    • 有两个手机,两个人抱着各自的手机的锁,当然互不影响了。

    锁class模板

    问题五

    • 将方法变为静态同步方法,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test3 {
        public static void main(String[] args) {
            Phone3 phone = new Phone3();
    
            // 线程A,人
            new Thread(()->{
                phone.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone.call();
            }, "B").start();
        }
    }
    
    // 手机
    class Phone3{
        // 静态同步方法
        public static synchronized void seedMsg(){
            // 4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
        // 静态同步方法
        public static synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    答案是发短信。

    为什么?

    那只是回答了一半,static静态方法,在程序刚执行的时候,就加载到内存中生成了.class模板

    这个class模板是唯一的,所以锁静态方法,其实锁的就是class模板。

     

    问题六

    • 现在有两个对象,调用不同对象的,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test3 {
        public static void main(String[] args) {
            Phone3 phone1 = new Phone3();
            Phone3 phone2 = new Phone3();
    
            // 线程A,人
            new Thread(()->{
                phone1.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone2.call();
            }, "B").start();
        }
    }
    
    // 手机
    class Phone3{
        // 静态同步方法
        public static synchronized void seedMsg(){
            // 4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
        // 静态同步方法
        public static synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    答案是发短信。

    为什么?

    因为锁的是class模板,而这个模板是唯一的一个,所以无论几个对象,使用的锁都是一样。

     

    锁方法的调用者和class模板

    问题七

    • 资源类中,一个是静态同步方法,一个普通同步方法,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test4 {
        public static void main(String[] args) {
            Phone4 phone1 = new Phone4();
    
            // 线程A,人
            new Thread(()->{
                phone1.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone1.call();
            }, "B").start();
        }
    }
    
    // 手机
    class Phone4{
        // 静态同步方法
        public static synchronized void seedMsg(){
            // 4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
        // 普通同步方法
        public synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    答案是打电话。

    为什么?

    两个锁的东西是不同,一个锁的是class模板,一个锁的是方法调用者,两个都是互不影响。

    可能有人会不确定,锁了class模板,虽然调用的是不同方法,但是可以调用吗?

    是可以的,创建的phone对象是共享class资源的。

    问题八

    • 在上面的基础上,增加一个对象,两个对象分别调用静态和非静态的同步方法,那么两个线程先打印 发短信 还是 打电话?

    package com.zxh.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    public class Test4 {
        public static void main(String[] args) {
            Phone4 phone1 = new Phone4();
            Phone4 phone2 = new Phone4();
    
            // 线程A,人
            new Thread(()->{
                phone1.seedMsg();
            }, "A").start();
            // 1秒延迟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程B,人
            new Thread(()->{
                phone2.call();
            }, "B").start();
        }
    }
    
    // 手机
    class Phone4{
        // 静态同步方法
        public static synchronized void seedMsg(){
            // 4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
        // 普通同步方法
        public synchronized void call(){
            System.out.println("打电话");
        }
    
    }

    答案是打电话。

    为什么?

    不要怀疑,以为增加了一个对象,就会有什么影响?

    不会的,只要关注锁的是什么,就不会有什么问题的。

    打电话和发短信的方法,锁的是不同的对象。不会有影响。

     8锁问题小结

    new 和 this 是一个对象。

    static 和 Class 是唯一的一个模板。

     

    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    JAVA学习日报 8.26
    JAVA学习日报 8.25
    JAVA学习日报 8.24
    JAVA学习日报 8.23
    Docker 详解
    DRF 3 请求响应异常处理
    DRF 2 序列化器
    DRF 1 API接口规范
    计算机计算小数的方法
    软件结构体系第二章
  • 原文地址:https://www.cnblogs.com/zxhbk/p/12952645.html
Copyright © 2020-2023  润新知