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

8锁现象

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

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

java 复制代码
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");
    }
}
java 复制代码
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!");
    }
}
java 复制代码
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");
    }
}
java 复制代码
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");
    }
}

小结

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

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

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

总而言之:

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

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

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

相关推荐
明日清晨25 分钟前
python扫码登录dy
开发语言·python
_李小白26 分钟前
【OSG学习笔记】Day 38: TextureVisitor(纹理访问器)
android·笔记·学习
bazhange39 分钟前
python如何像matlab一样使用向量化替代for循环
开发语言·python·matlab
人工干智能1 小时前
科普:python中你写的模块找不到了——`ModuleNotFoundError`
服务器·python
unicrom_深圳市由你创科技1 小时前
做虚拟示波器这种实时波形显示的上位机,用什么语言?
c++·python·c#
小敬爱吃饭1 小时前
Ragflow Docker部署及问题解决方案(界面为Welcome to nginx,ragflow上传文件失败,Docker中的ragflow-cpu-1一直重启)
人工智能·python·nginx·docker·语言模型·容器·数据挖掘
杨云龙UP1 小时前
从0到1快速学会Linux操作系统(基础),这一篇就够了!
linux·运维·服务器·学习·ubuntu·centos·ssh
宸津-代码粉碎机1 小时前
Spring Boot 4.0虚拟线程实战调优技巧,最大化发挥并发优势
java·人工智能·spring boot·后端·python
知行合一。。。1 小时前
Python--04--数据容器(集合)
python
头疼的程序员2 小时前
计算机网络:自顶向下方法(第七版)第八章 学习分享(三)
网络·学习·计算机网络