【并发编程篇】详细讲解八种锁现象,彻底理解锁

发布时间:2023年12月18日


在这里插入图片描述

在并发编程中,锁现象通常指的是多线程情况下可能出现的一些特定问题或现象。以下是八种常见的并发编程中的锁现象:

🍔1.标准情况下,两个线程先打印,先发短信还是先打电话

package org.example.lock8;

import java.util.concurrent.TimeUnit;

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

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

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendSms() {
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
发短信
打电话

🍔2.我们加上延时,看看有什么效果

在这里插入图片描述
这种情况,等待4秒后,先输出发短信,后输出打电话,而不是先输出打电话

为什么是先发短信,而不是先打电话呢
原因是因为先调用发短信的方法吗。不对,原因是因为synchronized锁的对象是方法的调用者(发短信和打电话这两个方法,都使用了synchronized),而且这两个方法都被phone所调用,由于这两个方法使用的是同一把锁,所以谁先拿到锁,谁先执行

🍔3.我们创建一个新方法,新方法没有使用synchronized

看看先打印什么出来

package org.example.lock8;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();

        new Thread(()->{
            phone.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}

class Phone2{
    public synchronized void sendSms() {
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }

    public void hello() {
        System.out.println("hello");
    }
}

在这里插入图片描述

结果
hello
发短信

因为这种情况下hello方法 没有加锁,不是同步方法,不受锁的影响

🍔4.两个对象的情况

package org.example.lock8;


import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(()->{
            phone1.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone2{
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
打电话
发短信

原因:现在有两个对象,两个调用者,即有两把锁,由于发短信方法 延迟了4秒钟(TimeUnit.SECONDS.sleep(4)😉,所以phone2先拿到锁,所以先执行 打电话

🍔5.一个对象,两个静态同步方法

其实就是在方法上加上static,变为静态方法

package org.example.lock8;

import java.util.concurrent.TimeUnit;

public class Test3 {
    public static void main(String[] args) {
        Phone3 phone = new Phone3();


        new Thread(()->{
            phone.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone3{
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
发短信
打电话

为什么是先发短息,这个static有什么影响吗
static是静态方法,类一加载就有了,那么锁的是class,是一个模板,发短信和打电话这两个方法的本质都是phone3,phone3只有唯一的class对象,这个class全局唯一,这两个方法都被static修饰了,所以这两个方法使用的是同一个锁

🍔6.两个对象,两个静态同步方法

package org.example.lock8;

import java.util.concurrent.TimeUnit;

public class Test3 {
    public static void main(String[] args) {
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        new Thread(()->{
            phone1.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone3{
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
发短信
打电话

如果它是一个普通的synchronized方法,它是由调用者对象来操作它
但是现在加上了static
这个锁 锁的的class对象
这个phone3的两个对象的class类模板只有一个,锁的是class,这个class全局唯一,这两个方法都被static修饰了,所以这两个方法使用的是同一个锁

🍔7.一个对象,1个静态同步方法,1个普通同步方法

package org.example.lock8;


import java.util.concurrent.TimeUnit;

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


        new Thread(()->{
            phone.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone4{
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
打电话
发短信

原因:静态同步方法锁的是class类模板
普通同步方法锁的是调用者
这里有两把锁,这两个方法用的不是同一把锁,所以先执行打电话,后执行发短信,发短信要延后4秒(TimeUnit.SECONDS.sleep(4)😉

🍔8.两个对象,1个静态同步方法,1个普通同步方法

package org.example.lock8;


import java.util.concurrent.TimeUnit;

public class Test4 {
    public static void main(String[] args) {
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();


        new Thread(()->{
            phone1.sendSms();
        },"A").start();

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

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone4{
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }
}

在这里插入图片描述

结果
打电话
发短信

同理,这里有2个锁,和第七个的情况一样

在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!

在这里插入图片描述

文章来源:https://blog.csdn.net/m0_72853403/article/details/135062680
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。