`
yesjavame
  • 浏览: 679201 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

JOIN的线程谁来唤醒

阅读更多

JOIN的线程谁来唤醒

这个问题在CSDN上被问到,我做了详细的回答。但提问者的水平不同,对正确答案的判断能力也不同,所他并没
有满意我的回答。
我之所以专门再为此写一篇文章,是从这个问题要引出话题还是应该要阐明的。


1.join做了什么?
打开jdk的源码,可以看到join其实就是在等待目标线程的结束:
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

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指定
的时间,如果没有指定时间,在线程的存活期内一直wait.

除了wait(time)可以到时被线程调度唤醒(有人说这叫自己醒来),否则当前线程一直在
while (isAlive()) {
wait(0);
}
这段代码是在目标线程中实现的供当前调用目标线程.join()的线程执行的,所以isAlive()当然是指目标线程
即,当前线程a调用线程b.join();那么isAlive()是线程b的。从线程b中看是this.isAlive(),如果从线程a中看就是
b.isAlive().同样wait是当前线程在目标线程对象上等待。那么当前线程等待什么?

当然是等待被notify(All)唤醒。没错,但这只是一个表面的现象,我要说的是等待条件。
等待条件决定是谁在换醒调用join的线程(我上面一直说的当前线程,其实调用后它就不是当前线程了)
这里因为等待条件是b.isAlive();所以当线程b is Not Alive时,一定会唤醒在它上面调用join的方法的线程a.

我们可以看到线程退出时的实现:
static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
thread->clear_pending_exception();
java_lang_Thread::set_stillborn(threadObj());
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread);
thread->clear_pending_exception();
}

这个本地方法在线程出时被调用,它除了做其它收尾工作,一定会唤醒在线程对象上wait的所有线程。因为是本地方法,
我们在JDK SRC中看不到被调用join的线程唤醒调用join的线程。但是

wait方法除了wait到期被线程调度唤醒外,始终等待wait的反条件。

同时,也把唤醒责任交给了改变那个条件的线程,所以如果要看一个wait中的线程被谁唤醒,就要看谁在控制这个条件,如果控制这个条件的线程始终没有唤醒调用wait的线程,那么就是一个到致命的设计错误。

这里,我们知道join其实最终是wait,是a调用b.wait,假如:
while(b.isAlive()){
wait();//其实就是this.wait();a.wait();
}
行不行?完全可以,只是b在退出时要知道谁在等待b is not Alive这个条件,然后通知这个线程醒来,因为b在自己的
实例上wait(),所以b要调用a.notify/All()才能唤醒,而如果在一开始没有一个地方标记了a在等待这个条件,b也就无法
知道a在等待这个条件,所以a才在b的对象上wait,但如果我们自己控制编程,有一个全局变量或数据结果保存等待这个条
件的线程句柄,那么完全可以在控制这个条件的线程中去唤醒,或者a可以在第三方不相关的对象上wait,然后控制这个条件
的线程在达到非wait条件时在这个第三方对象上调用notify/All。

所以真正唤醒join/wait中的线程的责任者是能够使等待条件不成立的线程。而wait中的线程真正等待的是反wait条件。
假如有一个List 对象 aList
while(aList.size() <= 0){
wait();
}
aList.removeFisrt();

我们知道现在这个线和要得到list的第一个元素,但如果list中没有元素就要wait.那么谁来唤醒它?当然是往list中加元素的
线程,所以如果有一个线程

aList.add(o);一定要调用notify/All();否则就是设计错误。至于调用谁的notify/All()就要看等待的线程在哪个对象上等待。

再次说明while(条件)的作用:

这是一个多线程版的安全的if(条件)。

在多线程环境下,如果只用if():

if(aList.size() <=0) aList.wait(); //假如就在list对象上wait,这也是一个非常好选择,因为add和remove都操作
//共同的对象是list,它也就成了共同可知的标记对象。
aList.removeFisrt();
线程a和线程b都执行到这里了。都在等待aList.size() > 0的条件。现在线程c往其中加了一个元素:
aList.add(o);
aList.notifyAll();

OK,线程a,b都被唤醒,执行wait()的后的代码,正确地从aList中remove了一个对象。然后切换到b运行,b也从wait后的代码
运行,结果aList没有元素了,b说,KAO,上当了,抛出异常吧。wait条件判断失败。

如果是while(aList.size() <=0) aList.wait();那么线程a从wait()后执行,又进循环,一看aList.size() >0了,所以直接执行
removeFirst,然后b也从wait后执行,又回到条件判断,一看aList.size() <= 0了,哦,只是我慢了一步,那我再等吧。

判断条件正确地得到了控制。

另外,处在wait中的线程不一定都是由控制循环条件的线程唤醒,比如你现在要等人,那个四点钟来,可是现在才两点,现在要睡觉,你叫你的同伴在四点的时候叫醒你。如果用

if(当前时间 < 四点)

我.wait();

meet a person;

在四点之前,不一定只有你的同伴会叫醒你,有可能在3点的时候一个酒鬼会过来吵醒你,这里你醒来了,执行下面meet a person;但因为时间条件还没有到,所以那个person为null,但如果是:

while(当前时间 < 四点)

我.wait();

meet a person;

不管在中点之前有多少人叫醒你,你都会看一下时间是否到四点,只有到了四点你才会meet a person.

所以应该成为习惯,应该坚持用while(条件)来wait而不是if.尽管 if偶尔也能正确工作(只有一个等待条件下)。

分享到:
评论

相关推荐

    Java线程中wait,await,sleep,yield,join用法总结.pdf

    本文将总结五个关键的线程控制方法:wait()、await()、sleep()、yield()和join()。这些方法各自有独特的用途和不同的工作机制。 一、wait()、notify()、notifyAll()用法 wait()、notify()和notifyAll()是Object类...

    Java线程之join_动力节点Java学院整理

    如果子线程仍然在运行,`join()`会调用`wait(0)`,这使得当前线程(这里是主线程)进入等待状态,释放CPU资源,直到被唤醒或超时。`wait()`方法实际上是`Object`类的方法,但它在这里由主线程调用,因此是主线程进入...

    浅谈java线程join方法使用方法

    在上面的代码中,我们首先创建了两个线程对象t1和t2,然后启动了t1线程,然后使用synchronized关键字来同步线程,最后使用notify方法来唤醒t1线程,最后启动t2线程。 join方法的应用场景 join方法的应用场景非常...

    Java线程唤醒与阻塞常用方法共2页.pdf.zip

    本文将深入探讨Java中用于线程唤醒与阻塞的常用方法,以及它们在实际编程中的应用。 1. **线程的阻塞** - `wait()`: 这个方法属于`Object`类,当一个线程调用对象的`wait()`方法时,它会释放该对象的锁并进入等待...

    Java多线程机制(讲述java里面与多线程有关的函数)

    线程联合(Join)是让一个线程等待另一个线程完成后再继续执行,通过调用`join()`方法实现。 9.11 守护线程: 守护线程(Daemon)是一种不阻止程序退出的线程,如垃圾收集器就是守护线程。当所有非守护线程结束时,...

    【IT十八掌徐培成】Java基础第08天-02.多线程-join-daemon-同步.zip

    此外,`wait()`和`notify()`(或`notifyAll()`)方法用于线程间的通信,让线程在等待资源时释放CPU,并在资源可用时唤醒。Java还提供了`ReentrantLock`可重入锁,它具有更高的灵活性,可以实现更复杂的同步策略。`...

    Python语言基础:线程等待.pptx

    在实际应用中,根据需求,我们可能需要对多个线程进行`join()`,以确保特定的执行顺序,或者确保某个线程的任务完成后才能进行其他操作。 线程等待的案例总结如下: 1. **线程抢占**:在多线程环境下,线程的执行...

    线程学习小Test

    本教程将通过`wait()`, `notify()`, `sleep()` 和 `join()` 这四个关键方法的示例来探讨如何在Java中实现线程同步。 首先,`wait()`, `notify()` 和 `notifyAll()` 方法是Object类的一部分,它们用于线程间通信。当...

    Java的多线程-线程间的通信.doc

    Java提供了多种机制来实现线程间的通信,包括使用wait(), notify()和notifyAll()方法,以及使用synchronized关键字控制并发访问共享资源。这些方法都定义在java.lang.Thread类中。 1. **wait(), notify()和...

    c++多线程编程的十个例子

    一个线程可以等待特定条件满足,而其他线程则负责改变这些条件,从而触发唤醒。 8. **线程池** 线程池是一种管理线程的方法,它预先创建一组线程,当有任务到来时,任务被分发到空闲线程中执行。这可以减少线程...

    Java多线程知识点总结

    同样,当线程使用wait方法时,它会主动释放锁并进入等待状态,直到其他线程调用notify或notifyAll方法来唤醒它。此外,sleep方法可以使线程进入阻塞状态一段时间,而join方法则允许一个线程等待另一个线程完成其任务...

    C#多线程C#线程及访问杂记

    - **Thread.Join**:一个线程可以调用另一个线程的Join方法,等待该线程完成后再继续执行。 6. **线程优先级** - C#中的Thread类提供了设置线程优先级的方法,如Priority属性,但优先级并不保证绝对的执行顺序,...

    java线程线程安全同步线程

    `wait()`和`notify()`/`notifyAll()`方法用于线程间的通信,使得线程可以在特定条件下释放资源并等待其他线程唤醒;`ReentrantLock`可重入锁提供了更灵活的控制,支持公平锁和非公平锁策略。 线程优先级是调度的...

    线程的几种控制方式以及线程间的几种通信方式

    7. **线程join()**:在Java中,`thread.join()`方法使得当前线程等待指定线程结束再继续执行,Python中没有直接对应的方法,但可以通过`Event`或`Condition`对象实现类似功能。 8. **守护线程(Daemon)**:Java中...

    Linux多线程Linux多线程

    - **线程Join**:主线程可以使用`pthread_join()`等待子线程结束并回收资源。 6. **线程的调度**: Linux内核的线程调度策略包括SCHED_OTHER(默认的抢占式调度)、SCHED_FIFO(先进先出,实时调度)和SCHED_RR...

    Java工程师线程试题

    线程的生命周期由`start()`、`run()`、`sleep()`、`join()`、`interrupt()`等方法共同控制。 理解并熟练掌握这些Java线程知识对于Java工程师来说至关重要,因为线程控制是多任务并行处理和系统优化的关键。在实际...

    POSIX多线程程序设计随书源码

    7. **线程调度**:POSIX允许开发者自定义线程调度策略,通过`pthread_setschedparam`和`pthread_getschedparam`来设置和查询线程的调度参数。 8. **线程优先级**:在某些系统中,可以通过`pthread_setschedprio`...

    CC++多线程编程练习题大全

    8. **线程 join 和 detach**:`join`方法使得主线程等待子线程完成,`detach`则将线程分离,不再与主线程关联,线程结束时资源会自动回收。 9. **死锁**:当两个或更多线程相互等待对方释放资源而陷入僵局时,发生...

    C++如何创建杀死线程

    当线程对象不再需要时,可以通过调用其`join`成员函数来等待线程完成。这是终止线程最安全的方式之一,因为它确保了线程资源被正确释放,避免了资源泄露。 ```cpp // 在线程结束后调用join t.join(); ``` #### 2.2...

    Linux线程.pdf

    - **条件变量(Condition Variable)**:配合互斥锁使用,使线程能够等待特定条件的发生,当条件满足时唤醒等待的线程。 总之,Linux线程提供了丰富的API和机制,使得开发者能够在Linux平台上高效地开发多线程应用...

Global site tag (gtag.js) - Google Analytics