• JUC并发编程——8锁现象(基于狂神说的学习笔记)


    8锁现象

    深刻理解锁究竟是什么,锁的对象究竟是谁

    8个锁的问题,两两分组,问题,答案,代码皆在下面代码块中,如有问题后续补充

    package Lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 8锁,就是关于锁的8个问题:
     * 1、标准情况下,两个线程先打印哪一个?---->先打印sendSms
     * 2、sendSms延迟4秒后,两个线程哪个线程先打印?---->先打印sendSms,因为synchronized锁锁的是资源对象
     */
    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) {
                throw new RuntimeException(e);
            }
            new Thread(()->{
                phone.call();
            },"B").start();
        }
    }
    
    class Phone{
    
        // synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
        // 两个方法用的是同一个锁,谁先拿到谁执行
        public synchronized void sendSms(){
    //--------------------------- 2 --------------------------
            // 让sendSms加4秒延迟
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    //--------------------------- 2 --------------------------
            System.out.println("send a sms");
        }
    
        public synchronized void call(){
            System.out.println("call");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    package Lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 3、增加了一个普通方法后,是先执行发短信还是执行hello--->hello 先输出,因为hello并不是同步方法,不受锁的影响,而sendSms有睡眠延迟
     * 4、两个对象,两个同步方法,先执行发短信还是执行打电话?----> call 先输出,这里有两个不同的对象,也就意味着有两把锁,A与B拿的锁不一样
     */
    public class Test02 {
    
        public static void main(String[] args) {
    // ------------------------- 3 -----------------------------------
    //        // 资源类
    //        Phone2 phone = new Phone2();
    //        // 创建并开启线程
    //        new Thread(()->{
    //            phone.sendSms();
    //        },"A").start();
    //
    //        try {
    //            TimeUnit.SECONDS.sleep(1);
    //        } catch (InterruptedException e) {
    //            throw new RuntimeException(e);
    //        }
    //        new Thread(()->{
    //            phone.hello();
    //        },"B").start();
    // ------------------------- 3 -----------------------------------
    // ------------------------- 4 -----------------------------------
            // 资源类
            Phone2 phone1 = new Phone2();
            Phone2 phone2 = new Phone2();
    
            // 创建并开启线程
            new Thread(()->{
                phone1.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
    // ------------------------- 4 -----------------------------------
    
        }
    }
    
    class Phone2{
    
        // synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
        // 两个方法用的是同一个锁,谁先拿到谁执行
        public synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("send a sms");
        }
    
        public synchronized void call(){
            System.out.println("call");
        }
    
        // 这里没有锁,不是同步方法,不受锁的影响
        public void hello(){
            System.out.println("this function is saying hello!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    package Lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     *5、增加两个静态的同步方法,只有一个对象,先打印哪一个?----->
     * 先发短信,因为当将方法声明为static时,则该方法全局唯一,与Class一同加载,换句话说,该锁锁的是类,而非方法
     *6、两个对象,增加两个静态的同步方法,先打印哪一个?----->
     * 先发短信,同样的,因为static的缘故,synchronized锁住的是类,而类只有一个,因此实际上只有一把锁
     */
    public class Test03 {
    
        public static void main(String[] args) {
    // ------------------------- 5 -----------------------------------
    //        // 资源类
    //        Phone3 phone = new Phone3();
    //
    //        // 创建并开启线程
    //        new Thread(()->{
    //            phone.sendSms();
    //        },"A").start();
    //
    //        try {
    //            TimeUnit.SECONDS.sleep(1);
    //        } catch (InterruptedException e) {
    //            throw new RuntimeException(e);
    //        }
    //        new Thread(()->{
    //            phone.call();
    //        },"B").start();
    // ------------------------- 5 -----------------------------------
    // ------------------------- 6 -----------------------------------
            // 资源类
            Phone3 phone1 = new Phone3();
            Phone3 phone2 = new Phone3();
    
            // 创建并开启线程
            new Thread(()->{
                phone1.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
    // ------------------------- 6 -----------------------------------
    
        }
    }
    
    // Phone3 唯一的一个class对象
    // 在Java的世界里,万物皆对象,类也不例外,类有两个对象,实例对象以及Class对象
    // Class对象包含了与类有关的信息,实例对象就是通过Class对象创建的
    class Phone3{
    
        // synchronized 锁的对象是方法的调用者
        // static 静态方法
        // 类一加载就有了,锁的是Class
        public static synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("send a sms");
        }
    
        public static synchronized void call(){
            System.out.println("call");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    package Lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     *7、 一个静态同步方法,一个普通同步方法,一个对象,谁先打印?----->
     * 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
     *8、 一个静态同步方法,一个普通同步方法,两个对象,谁先打印?----->
     * 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
     */
    public class Test04 {
    
        public static void main(String[] args) {
    
    // ------------------------- 7 -----------------------------------
    //        // 资源类
    //        Phone4 phone = new Phone4();
    //
    //        // 创建并开启线程
    //        new Thread(()->{
    //            phone.sendSms();
    //        },"A").start();
    //
    //        try {
    //            TimeUnit.SECONDS.sleep(1);
    //        } catch (InterruptedException e) {
    //            throw new RuntimeException(e);
    //        }
    //        new Thread(()->{
    //            phone.call();
    //        },"B").start();
    // ------------------------- 7 -----------------------------------
    // ------------------------- 8 -----------------------------------
            // 资源类
            Phone4 phone1= new Phone4();
            Phone4 phone2 = new Phone4();
    
            // 创建并开启线程
            new Thread(()->{
                phone1.sendSms();
            },"A").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
    // ------------------------- 8 -----------------------------------
    
        }
    }
    
    class Phone4{
        // 静态同步方法  锁的是Class类模板
        public static synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("send a sms");
        }
    
        // 普通同步方法  锁的是调用者
        public synchronized void call(){
            System.out.println("call");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    小结

    new 出来的对象,是一个实例对象,实例可以有很多个,因此synchronized锁只是锁在实例对象上,如果两个线程持有的实例对象资源不同,则两个线程独立,并不会被对方的锁干扰

    static 则表名该方法与Class同步产生,因此,static是在Class对象中的,如果synchronized锁锁住了static方法,则表明锁住了整个Class对象,而因为Class对象唯一(所有的实例对象都通过Class对象创建的)因此,无论后续创建了多少个实例对象资源,最终只要某一线程持有住某一静态同步方法的锁,则其他静态同步方法一律无法使用。

    而同时,因为Class对象和实例对象本质上是两个对象(虽然实例对象是通过Class对象创建而来的),因此某一线程持有某一静态同步方法的锁,也无法影响其他普通同步方法

    总而言之:

    普通同步锁实例,静态同步锁模板,

    即使再多实例在,静态之间统一管

    静态普通不同属,相互之间无法管

  • 相关阅读:
    数据可视化实战:实验报告
    飞行态势知识图谱及其问答系统的构建方法
    硬核!8个类手写一个配置中心!
    网络基础-2
    NFVA35065L32 IGBT 650V 汽车智能功率模块 (ASPM) 特征
    ceph 1 pool(s) do not have an application enabled
    uni-app 如何更换tabbar里面的图标
    4-操作列表(循环结构)
    06条件判断
    矩阵分析与应用+张贤达
  • 原文地址:https://blog.csdn.net/whale_cat/article/details/133800380