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

前言

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

锁,这个概念比较抽象,拿到锁,就意味着拿到了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)的区别(四)