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 是唯一的一个模板。

 

 

posted @ 2020-05-24 21:04  忘忧山的兰木  阅读(495)  评论(0编辑  收藏  举报
她只是想吃这个而已啊……这一定是她非常爱吃的,我居然连如此细微的幸福也夺走了……
Hide
Switch
Save