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

本文通过实例代码深入探讨了Java多线程中join()方法的使用,解释了join()如何影响锁的释放。当主线程对object加锁时,调用join()导致死锁;而对mythread线程对象加锁时,join()能正常工作,使得线程按预期顺序执行。理解这一点对于避免死锁和正确控制线程执行顺序至关重要。
摘要由CSDN通过智能技术生成

前言

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

锁,这个概念比较抽象,拿到锁,就意味着拿到了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);
			}
		}
	}
}

二、运行结果

2.1、main方法对object添加锁,代码卡死在20不动了,造成死锁(不释放锁)

注:运行前,需要确保上述代码中的main方法中的代码同步块是object,如下所示!

synchronized (object)

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

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

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

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

2.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!

尾言

学好多线程,这些都是绕不过去的点,只能深挖后一个一个突破,加油!

1、 JAVA多线程:synchronized理论和用法 | Lock和ReentrantLock Volatile 区别和联系(一)

2、JAVA多线程:yield/join/wait/notify/notifyAll等方法的作用(二)

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

4、JAVA多线程:sleep(0)、sleep(1)、sleep(1000)的区别(四)

  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值