人不敬我,是我无才,我不敬人,是我无德。
人不容我,是我无能,我不容人,是我无量。
人不助我,是我无为,我不助人,是我无善。
1. join()
Waits for this thread to die.(等待调用线程消亡…)
当我们在一个线程(宿主线程)上调用 join() 方法,则该线程(宿主线程)进入等待状态,直到调用join()的线程(调用线程)终止。
有很多朋友对Thread的join()理解不到位,原因就在于闹不清楚这里面的两个名词:
- 宿主线程
- 调用线程
下列代码:
class JoinThread extends Thread{
public int processingCount = 0;
// 宿主线程
public Thread container;
public JoinThread(int processingCount,String name,Thread container) {
this.processingCount = processingCount;
this.setName(name);
this.container = container;
System.out.println("Thread Created...");
}
@Override
public void run() {
System.out.println("Thread- "+this.getName()+" started...");
while (processingCount > 0){
try{
System.out.println(container.getName()+" 宿主线程的状态:"+container.getState());
System.out.println("调用线程的状态:"+this.getState());
Thread.sleep(1000L);
System.out.println("Current Value:"+processingCount);
}catch (InterruptedException e){
System.out.println("Thread- "+ this.getName() +" interrupted...");
}
processingCount --;
}
System.out.println("Thread "+ this.getName()+" exiting...");
}
}
调用代码:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new JoinThread(5,"Thread-A",Thread.currentThread());
t1.start();
System.out.println("Invoking join...");
t1.join();
System.out.println("Returned from join...");
System.out.println(t1.isAlive());
}
此处的宿主线程一直处于等待状态,直到调用线程结束。注意此处也需要处理异常:InterruptedException
如果调用线程阻塞或者需要花费太多时间去处理,则 join() 将会一直等待,这将会导致一个问题——调用线程无响应。
2. join() 的同胞兄弟
- join(long millis) : 为了等待调用线程消亡,最多 millis 毫秒(0 :意味着永远等待)
- join(long mills, int nanos) :为了等待调用线程消亡,最多 millis + nanos 纳秒。
定时的 join() 方法 依赖于 OS的计时,因此我们不能精确地等待指定的时间。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new JoinThread(5,"Thread-A",Thread.currentThread());
t1.start();
System.out.println("Invoking join...");
// 注意这里
t1.join(1000L);
System.out.println("Returned from join...");
System.out.println(t1.isAlive());
}
结果如下:
3. join()的同步效果
除了(In addition to )等待调用线程消亡,调用 join()方法还有一个同步效果。join()创建了一种happens-before的关系。
3.1 What’s the happens-before关系
简而言之,如果在线程1中调用了线程2.join(),则线程2中的所有变化对于线程1都是可见的。反之,如果我们不去调用join()或者其它同步策略,我们不能保证在当前线程中能观察到其它线程的正确状态。
因此(Hence),即使调用join的线程能立即返回终止状态,我们有时仍然需要调用join()方法。
运行下面的程序,可以加深对此的理解
static class MyThread extends Thread{
public int count = 0;
public MyThread(int count) {
this.count = count;
}
@Override
public void run() {
while (count>0){
try {
System.out.println("Current value:"+count);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count --;
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread(10);
myThread.start();
// 如果不调用join()相关方法, 即使myThread结束,也不能保证程序停止
do{
System.out.println("Running always...?");
// myThread.join();
// myThread.join(1000);
}while (myThread.count >0);
}
这里,主线程对MyThread的状态不敏感
4. join()的源码解析
通过前面的分析,我们可以看出join() 方法会引起宿主线程的阻塞,我们就来看一下:它是如何实现这一功能的。
4.1 join()源码
// 注意这里的“synchronized”关键字,加锁当前线程对象
/**
* @param : mills 等待时长(0-表示永远等待)
*/
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0); // 注意这里的wait()
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
我们可以发现阻塞功能是由Object 的wait() 方法来实现的。调用wait()方法必须要获取锁,所以join()方法被synchronized关键字修饰。另外我们知道,wait()要和**notify()或者notifyAll()**一般都是配合使用,进行 唤醒操作。但是貌似没看到它的身影啊。
4.2 join()的wait()的唤醒操作在哪里?
我们可以先来看一下线程退出时,会做什么操作?
- 线程退出的方法:exit()
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
...
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
ensure_join(this);
assert(!this->has_pending_exception(), "ensure_join should have cleared");
...
}
- ensure_join()的实现
ensure_join()内部调用了lock.notify_all(thread)进行唤醒操作
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);// 调用了notify_all(thread)
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
我们会发现,当线程终止时,会调用线程自身的notifyAll()方法,通知所有等待该线程对象上的线程。
5. 关于join()的总结
- join()是Thread的方法,其方法被asynchronized关键字修饰。
- join()还是通过等待/通知机制来实现其逻辑的,本质上还是加锁——>循环——>处理这3个步骤。(wait,notifyAll)
参考文章: