原文: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!