JUC并发编程——通过八锁现象更好的理解 Synchronized

JUC并发编程——八锁现象

八锁现象

八锁现象,其实就是关于锁的八个问题,通过八个问题,逐渐加深对锁的认识,为了方便演示,这里全部用synchronized。

案例一:两个synchronized 同步方法 + 两个线程 + 一个方法调用者

package com.cheng.Lock8;

public class Test1 {
    public static void main(String[] args) {
        BackHome backHome = new BackHome();
        new Thread(()->{
            backHome.car();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            backHome.bus();
        },"B").start();
    }
}

class BackHome{

    public synchronized void car(){
        System.out.println("开车回家");
    }

    public synchronized void bus(){
        System.out.println("坐公家回家");
    }

}

问题一:正常情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

问题二:car() 同步方法延迟3秒,线程先打印"开车回家"还是"坐公家回家"?

public synchronized void car(){
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("开车回家");
}

启动程序打印测试:

在这里插入图片描述

解释:synchronized 锁的对象是方法的调用者,也就是上面的 backHome 对象, backHome 对象只有一个,因此锁也只有一个,两个方法用的是用一把锁,谁先拿到谁就先执行。

案例二:两个同步方法 + 一个普通方法 + 两个线程 + 一个方法调用者

package com.cheng.Lock8;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        BackHome2 backHome = new BackHome2();
        new Thread(()->{
            backHome.car();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            backHome.bus();
        },"B").start();

        new Thread(()->{
            backHome.taxi();
        },"C").start();
    }
}

class BackHome2{
    public synchronized void car(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("开车回家");
    }

    public synchronized void bus(){
        System.out.println("坐公家回家");
    }

    //普通方法
    public void taxi(){
        System.out.println("坐出租车回家");
    }
}

问题三:car() 同步方法延迟3秒,线程先打印"开车回家"还是"坐出租车回家"?

启动程序打印测试:

在这里插入图片描述

解释:普通方法没有 synchronized 关键字,不受锁的影响,又因为car() 同步方法延迟3秒,所以线程先执行普通方法。

案例三:两个同步方法 + 两个线程 + 两个方法调用者

backHome1 调用 car(),

backHome2 调用 bus()

package com.cheng.Lock8;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        BackHome2 backHome1 = new BackHome2();
        BackHome2 backHome2 = new BackHome2();
        
        new Thread(()->{
            backHome1.car();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            backHome2.bus();
        },"B").start();
    }
}

class BackHome2{

    public synchronized void car(){
        
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("开车回家");
    }

    public synchronized void bus(){
        System.out.println("坐公家回家");
    }
}

问题四:两个方法调用者情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

一秒后打印"坐公家回家",三秒后再打印 “开车回家”

解释:有两个方法调用者backHome1和backHome2,因此两个同步方法有两把锁,而因为car()方法有延迟,所以bus()方法先执行。

案例四:两个静态同步方法 + 两个线程 + 一个方法调用者

package com.cheng.Lock8;

import java.util.concurrent.TimeUnit;

public class Test3 {
    public static void main(String[] args) {
        BackHome3 backHome = new BackHome3();
  
        new Thread(()->{
            backHome.car();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            backHome.bus();
        },"B").start();

    }
}

class BackHome3{

    public static synchronized void car(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("开车回家");
    }

    public static synchronized void bus(){
        System.out.println("坐公家回家");
    }
}

问题五:静态同步方法下情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

在案例四的基础上再加一个方法调用者:

BackHome3 backHome = new BackHome3();
BackHome3 backHome1 = new BackHome3();

new Thread(()->{
    backHome.car();
},"A").start();

try {
    TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
    e.printStackTrace();
}

new Thread(()->{
    backHome1.bus();
},"B").start();

问题六:静态同步方法下情况下两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

解释:加了 static 的同步方法,在类一加载就有了,锁的对象不再是方法的调用者,而是Class的类模板,不管有几个方法调用者,锁的对象都是这个类模板且全局唯一。

案例五:一个静态同步方法 + 一个普通同步方法 + 一个方法调用者

package com.cheng.Lock8;

import java.util.concurrent.TimeUnit;

public class Test4 {
    public static void main(String[] args) {
        BackHome4 backHome = new BackHome4();

        new Thread(()->{
            backHome.car();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            backHome.bus();
        },"B").start();

    }
}

class BackHome4{

    public static synchronized void car(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("开车回家");
    }

    public  synchronized void bus(){
        System.out.println("坐公家回家");
    }
}

问题七:一个静态同步方法 + 一个普通同步方法情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

在案例五的基础上新增一个方法调用者:

BackHome4 backHome = new BackHome4();
BackHome4 backHome1 = new BackHome4();

new Thread(()->{
    backHome.car();
},"A").start();

try {
    TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
    e.printStackTrace();
}

new Thread(()->{
    backHome1.bus();
},"B").start();

问题八:一个静态同步方法 + 一个普通同步方法情况下 + 两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

在这里插入图片描述

解释:一个静态同步方法 + 一个普通同步方法情况下,锁的对象不同,静态同步方法锁的是全局唯一的类模板,普通同步方法锁的是方法的调用者,因此就算有两个方法调用者,锁也不会冲突,所以没有延迟的方法先执行。

总结

对于普通同步方法,锁是当前实例对象

对于静态同步方法,锁是当前类的class对象

对于同步方法块,锁是括号里面的对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

万里顾—程

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值