JUC高级多线程_03:关于多线程锁的八个常见问题

Posted ABin-阿斌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC高级多线程_03:关于多线程锁的八个常见问题相关的知识,希望对你有一定的参考价值。

我是 ABin-阿斌:写一生代码,创一世佳话,筑一揽芳华。 如果小伙伴们觉得我的文章有点 feel ,那就点个赞再走哦。
在这里插入图片描述

1 . 多线程情况下关于锁会出现的八项问题:

代码演示:

public class JUC04_Lock8 {
    public static void main(String[] args) throws Exception {
        Phone phone = new Phone();

        //问题四:
        // 新增一个资源类
        Phone phone2 = new Phone();

        new Thread(()->{
            phone.sendMes();
//            phone.sendMes2();

            },"Thread_A").start();

        //问题一:
        // 加一个时间间隔,保证 A 先启动
        Thread.sleep(200);

        new Thread(()->{
//            phone.sendEmall();
//            phone.hello();
//            phone2.sendEmall();
//            phone.sendEmall2();

            //问题六:
            // 两个资源类
//            phone2.sendEmall2();

            //问题七:
            // 1 个普通方法,1 个静态方法, 1 个资源类
//            phone.sendEmall2();

            //问题八:
            // 1 个普通方法,1 个静态方法, 2 个资源类
            phone2.sendEmall2();

            },"Thread_B").start();

    }
}
class Phone{
    public synchronized void sendEmall(){
        System.out.println("-----发送邮件-----");
    }
    public synchronized void sendMes(){
        //问题二:
        // 让发送短信暂停 4 秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("++++++发送短信++++++");
    }

    //问题五:
    // 两个静态方法类
    public synchronized void sendEmall2(){
        System.out.println("--静态---发送邮件-----");
    }
    public synchronized void sendMes2(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("++静态++++发送短信++++++");
    }


    //问题三:
    // 新建一个 hello 方法
    public void hello(){
        System.out.println("hello");
    }
}

1. 问题一:正常访问,先打印短信还是邮件

  • 答案:短信

2. 问题二:停4秒在短信方法内,先打印短信还是邮件

  • 答案: 短信
  • 针对第一、二问题原理说明:
    • synchronized 锁的不是单个方法,而是该方法所在的整个资源类
    • 所以某一个时刻内,只要一个线程去调用其中的一个 synchronized 方法了,其它的线程都只能等待
    • 换句话说,某一个时刻内,只能有唯一一个线程去访问这些 synchronized 方法
    • 锁的是当前对象 this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized 方法

3. 问题三:普通的hello方法,是先打短信还是hello

  • 答案: Hello
  • 针对第三个问题原理说明:
    • 普通方法和同步锁无关

4. 问题四:现在有两部手机,先打印短信还是邮件

  • 答案: 邮件
  • 针对第四个问题原理说明:
    • 换成两个对象后,不是同一把锁了,每个线程都有自己的资源类
    • 情况立刻变化,互不相干

5. 问题五:两个静态同步方法,1部手机,先打印短信还是邮件

  • 答案: 短信

6. 问题六:两个静态同步方法,2部手机,先打印短信还是邮件

  • 答案: 短信

7.问题七:1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件

  • 答案: 邮件

8. 问题八:1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件

  • 答案: 邮件
  • 针对第五、六、七、八这四个问题的原理说明:
    • 加上 static 后该方法就不是针对实例化的对象,而是针对整个类
    • 所以 加上 staticsynchronized 锁的不是具体的实例化对象,而是锁的整个 类
    • synchronized 实现同步的基础: Java 中的每一个对象都可以作为锁。
    • 具体表现为以下三种形式:
      • 对于普通同步方法,锁是当前实例对象。
      • 对于静态同步方法,锁是当前类的 Class 对象。
      • 对于同步方法块,锁是 Synchonized 括号里配置的对象

9. 总结:

注意事项:
  • 当一个线程试图访问同步代码块时,必须先得到锁,当退出或抛出异常时必须释放锁 。也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁。
  • 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以不必要等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
  • 所有的静态同步方法用的也是同一把锁:类对象本身
  • 这两把锁(this / class)是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
  • 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象。

以上是关于JUC高级多线程_03:关于多线程锁的八个常见问题的主要内容,如果未能解决你的问题,请参考以下文章

juc并发编程学习笔记下(尚硅谷)

Java多线程系列--“JUC锁”03之 公平锁

Java多线程系列--“JUC锁”05之 非公平锁

JUC高级多线程_01:基础知识回顾

JUC高级多线程_06:多线程下得常用辅助类

JUC高级多线程_02:线程间的通信