首页 » SEO优化 » phpthreadjoin技巧_Thread的join方法事理

phpthreadjoin技巧_Thread的join方法事理

duote123 2024-11-18 0

扫一扫用手机浏览

文章目录 [+]

join方法开释锁吗?

前段韶光,有一个读者私信我,问了这么一个问题:Thread实例的join方法内部是调用的wait方法,而wait方法是会开释锁的,为什么网上很多文章(包括我们之前写的开源书《深入浅出Java多线程》)会说join方法不开释锁?

开释thread工具锁

我们先用书中的一个例子提及:

phpthreadjoin技巧_Thread的join方法事理

public class Join { static class ThreadA implements Runnable { @Override public void run() { try { System.out.println("我是子线程,我先睡一秒"); Thread.sleep(1000); System.out.println("我是子线程,我睡完了一秒"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadA()); thread.start(); thread.join(); System.out.println("如果不加join方法,我会先被打出来,加了就不一样了"); }}

在这个例子中,我们在main方法中调用了thread.join(),打印出来的效果便是:

phpthreadjoin技巧_Thread的join方法事理
(图片来自网络侵删)

我是子线程,我先睡一秒我是子线程,我睡完了一秒如果不加join方法,我会先被打出来,加了就不一样了

这个例子想要表达的意图很大略,便是通过thread实例的join方法,达到main线程等待thread线程实行完后再连续实行的效果。

那join方法底层是如何实现这个功能的呢?究竟会不会开释锁呢?我们点进去看看源码。

if (millis == 0) { while (isAlive()) { wait(0); }} else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; }}

可以看到,join的底层是调用的wait(long)方法。
而wait方法是Object类型的实例方法,会开释当前Object的锁,且须要拿到当前Object的锁才行。

这么说可能有点绕。
众所周知,Java的锁实在实质上是工具锁,由于我们前面调用的是thread.join(),以是这里的“锁”工具实在thread这个工具。
那这里wait开释的是thread这个工具锁。

我们把上面的main方法大略改一下,用另一个线程是占住thread这个工具锁,就比较直不雅观了:

public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadA()); thread.start(); new Thread(() -> { // 把thread工具作为锁占住,这样下面的join里面的wait只有等锁开释了才能实行。
synchronized (thread) { try { System.out.println("我占住了thread锁"); Thread.sleep(10000); System.out.println("我thread锁开释了"); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); thread.join(); System.out.println("如果不加join方法,我会先被打出来,加了就不一样了");}

打印结果:

我是子线程,我先睡一秒我占住了thread锁我是子线程,我睡完了一秒我thread锁开释了如果不加join方法,我会先被打出来,加了就不一样了

这就印证了那句话:wait方法实行前,是须要获取当前工具的锁的。

以是回归到最开始的问题:join()方法会开释锁吗?严瑾的答案是它会开释thread实例的工具锁,但不会开释其它工具锁(包括main线程)。
stackoverflow也对这个有谈论:Does Thread.join() release the lock? Or continue to hold it?。

大略来说,你说它开释了锁也对,由于它确实通过wait方法开释了thread工具锁,你说它没开释锁也对,由于从调用线程的角度来看,它并没有开释当前调用线程持有的工具锁。

当然,为了防止其它读者看到这也有这个迷惑,我直接把文中的这句话删掉了。

谁唤醒了?

源码看到这,我又有了一个新的疑问:join方法内部是一个while循环。
wait开释了锁,那一定会有一个人来唤醒它,程序才能够连续往下走。
那一定有一个地方调用了thread工具的notify方法。

我们在Thread类里面可以找到一个exit()方法,上面备注写着:This method is called by the system to give a Thread a chance to clean up before it actually exits.

这么大略的英文大家该当都能看懂吧?

里面有这么一段代码:

if (group != null) { group.threadTerminated(this); group = null;}void threadTerminated(Thread t) { synchronized (this) { remove(t); if (nthreads == 0) { notifyAll(); } if (daemon && (nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) { destroy(); } }}

一开始我以为是在这里唤醒的,但仔细一看,这里调用的工具是ThreadGroup的实例,而不是thread实例。
以是该当不是这个地方。

经由一通google之后,我又在stackoverflow上找到了精确的答案(stackoverflow, yyds):who and when notify the thread.wait() when thread.join() is called?

答案显示,这是在JVM层面去做的事:

static void ensure_join(JavaThread thread) { // We do not need to grap the Threads_lock, since we are operating on ourself. Handle threadObj(thread, thread->threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); // Clear the native thread instance - this makes isAlive return false and allows the join() // to complete once we've done the notify_all below java_lang_Thread::set_thread(threadObj(), NULL); lock.notify_all(thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception();}

可以看到除了notify_all以外,它实在做了很多扫尾的事情。
包括处理非常、设置线程状态等。

如果线程没启动

再把代码改一下,如果线程没有通过start启动会若何呢?

Thread thread = new Thread(new ThreadA());// thread.start();thread.join();System.out.println("如果不加join方法,我会先被打出来,加了就不一样了");

会直接实行末了一行代码打印出来。

看join源码就知道了,在wait之前,会有一个isAlive()的判断,看当前哨程是否是alive的。
如果没有start,那就会直接返回false,不进入wait。

总结

join方法会开释thread工具锁,底层是wait方法,在JVM层面通过notify_all来唤醒的。

求个支持

我是Yasin,一个坚持技能原创的博主,我的wx"大众年夜众号是:编了个程

都看到这儿了,如果以为我的文章写得还行,不妨支持一下。

文章会首发到"大众年夜众号,阅读体验最佳,欢迎大家关注。

你的每一个转发、关注、点赞、评论都是对我最大的支持!

还有学习资源、和一线互联网公司内推哦

求个支持

我是Yasin,一个坚持技能原创的博主,我的微信"大众号是:编了个程

都看到这儿了,如果以为我的文章写得还行,不妨支持一下。

文章会首发到"大众年夜众号,阅读体验最佳,欢迎大家关注。

你的每一个转发、关注、点赞、评论都是对我最大的支持!

还有学习资源、和一线互联网公司内推哦

标签:

相关文章