• JAVA多线程:狂抓 join()方法到底会不会释放锁,给你彻底介绍清楚(三)


    原文:https://blog.csdn.net/succing/article/details/123023851

    前言
    了解这个问题前,先了解锁的概念:

    锁,这个概念比较抽象,拿到锁,就意味着拿到了CPU的执行权。拿3个人看电视来说,锁就好比遥控。

    A拿到遥控了,如果A仅仅是想休息一会儿,并不像放弃遥控的持有权,那么就调用sleep(1000)方法。然而,管理员来了,对A说,我限定你N秒内,不得再拥有遥控,此时就调用wait(10000)方法,调用wait后A会立刻丢失遥控的所有权(直到10秒后才会参与再次竞争),剩余的所有人按优先级,重新争取(锁)遥控的持有权。

    概述
    针对于这个问题,相信很多在使用多线程的人,都搞的不是很清楚,一直被这个问题困扰。

    先说结论

    join底层调用的是wait(),而wait是Object的方法,wait本身是会释放锁(彻底交出CPU的执行权),所以 Thread 的join() 方法是否会释放锁?答案是会!

    但是,join()只会释放Thread的锁,不会释放线程对象的锁(可能会造成死锁)。

    相信很多人看到这个答案,比较绕口,看了很懵逼,上代码(代码很简单,耐心一点哦)

    一、示例代码
    public class ThreadJoinTestLock {

    public static void main(String[] args) {
    Object object = new Object();
    MThread mythread = new MThread("mythread ", object);
    mythread.start();
    //synchronized (mythread)
    synchronized (object) {
    for (int i = 0; i < 100; i++) {
    if (i == 20) {
    try {
    System.out.println("开始join");
    mythread.join();//main主线程让出CPU执行权,让mythread子线程优先执行
    System.out.println("结束join");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    System.out.println(Thread.currentThread().getName() +"==" + i);
    }
    }
    System.out.println("main方法执行完毕");
    }
    }

    class MThread extends Thread {
    private String name;
    private Object obj;
    public MThread(String name, Object obj) {
    this.name = name;
    this.obj = obj;
    }
    @Override
    public void run() {
    synchronized (obj) {
    for (int i = 0; i < 100; i++) {
    System.out.println(name + i);
    }
    }
    }
    }
    二、运行结果
    1.main方法对object添加锁,代码卡死在20不动了,造成死锁(不释放锁)
    注:运行前,需要确保上述代码中的main方法中的代码同步块是object,如下所示!

    synchronized (object)


    可以看到,在join之前,一直是主线程在执行,main方法中的for循环到20时,此时该join方法的作用是让main主线程阻塞,给被join的线程让出CPU的执行权,让子线程mythread先执行。

    但是,结局很意外,main方法的object不释放锁,已经进入了阻塞状态,因为object没有释放锁,子线程又拿不到锁,所以就卡死了,其实是主线程阻塞,子线程得不到锁(CPU的运行机会)。

    此时的主线程仿佛在对子线程说:你咬我呀,我就是占着茅坑(锁)不拉屎(运行),子线程说,你有本事把茅坑让给我呀,主线程说,我就是不让。两者相斥不下,就卡死了。

    如果注释掉join相关代码:则可以看到主线程执行完毕(一口气把锁用完,然后交出锁),才会执行子线程。


    2.main方法对mythread(子线程)添加锁,代码顺利跑完(释放锁)
    注:运行前,需要确保上述代码中的main方法中的代码同步块是mythread

    synchronized (mythread)
    可以看到,主线程for循环,在i=20之前,主线程和子线程是交替执行的。

    i=20,mythread调用了join,貌似在说,main老兄你先歇一歇(阻塞),我运行完毕你再来运行。

    看到的结果就是,直到子线程运行完毕,主线程才接着从20开始运行。

    三、结论
    最后,再回顾一下main方法中的代码sysnchronized代码。

    synchronized (object)
    synchronized (mythread)
    失之毫厘谬以千里,仅仅是synchronized对象的不同,结果就造成这么大的差异。

    此时,再回顾一下开头的结论: join()会释放Thread的锁,不会释放线程对象的锁(可能会造成死锁)。

    此时,是不是豁然开朗了?

    所以针对于这个问题,要看join的外层,synchronized作用的对象,是object实体对象,还是thread!

  • 相关阅读:
    Android中Bitmap对象和字节流之间的相互转换
    Android 6.0以后的版本报错:open failed: EACCES (Permission denied)
    C#—ASP.NET:集成极光推送(Push API v3)
    极光推送(C#)
    模仿今日头条导航栏滑动显示更多
    使用VMWare虚拟mac系统,设置网络的正确姿势
    vmware panic(CPU 0 caller 0x)launchd exited
    VMware15安装MAC(MAC OS 10.13)(OS X 10.14)原版可升级最新可解锁macOS Unlocker3.0(OS X 10.13)
    Flutter Dart List.map() 获取下标
    Flutter利用GridView实现网格的商品布局
  • 原文地址:https://www.cnblogs.com/fuyuanming/p/16138469.html
Copyright © 2020-2023  润新知