话不多说,直接上代码。
/**
* 等待线程死亡。
*/
public final void join() throws InterruptedException {
// 参数0表示永久等待
join(0);
}
/**
* 下面才是正戏
* 等待最多millis的时间,如果millis为0,则一直等待
*
* 通过调用控制条件为isAlive()的wait()方法来实现.当线程终止时,就会调用notifyAll方法.建议在应用中实例不要使用wait()方法
* 或者notifyAll()方法(原因在后面讲wait()方法的时候会详细说)
* @param millis
* 等待时间(毫秒为单位)
*
* @throws IllegalArgumentException
* 如果millis为负数,则抛出异常
*
* @throws InterruptedException
* 如果有线程中断了当前线程。抛出此异常时清除当前线程的中断状态。
*
*/
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) {
// isAlive()指已经started还未died
while (isAlive()) {
// 参数为0表示一直等待
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
可以从源代码中看出,join方法是调用了Object类中的wait()方法的,再跟踪一下wait()方法。下面提到的wait方法的源代码都非java代码,所以没有直接代码,只能通过注释来理解。
/**
* 将使当前线程等待要么其他线程调用notify方法,要么等待调用notifyAll方法,或者是时间到。
*
* 当前线程必须是对象的监管者。
*
* 该方法将使当前线程(以下称之为线程T)加入该对象的等待队列中然后放弃该对象的所有资源(这是跟sleep方法的不同之处,sleep方法不会放弃资源)。然后
* 该线程就不可被调度,在以下四件事之一发生前将一直处于休眠状态:
*
* 1.有其他线程调用了该对象的notify方法且线程T也正好被选中为唤醒。
*
* 2.有其他线程调用了该对象的notifyAll方法。
*
* 3.有其他线程打断了线程T。
*
* 4.到了millis时间。但如果millis为0,则线程就一直等待,知道收到notify或notifyAll。
*
* 然后线程T就从等待队列中被移除,变成可被调度状态,然后跟其他线程公平的竞争对象锁。一旦该线程获得了对线的控制权,它对对象的
* 所有同步请求都恢复到之前的状态,也就是调用该方法时的状态。然后线程T从wait方法的调用中返回。因此,从wait方法返回时,对象和
* 线程T的同步状态与调用等待方法时完全相同。
*
* 线程在未被notify、interrupt或者超时时也可能会被唤醒,这就是我们所谓的假性唤醒。由于这在实际应用中偶尔会出现,所以应用必
* 须在唤醒线程前先检查条件是否符合,只有当条件满足的时候才继续唤醒。也就是说,wait方法总是在循环中出现,例如:
*
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
*
* 如果一个线程在等待时被打断,会抛出InterruptedException异常,在如上所述恢复此对象的锁状态之前,不会抛出此异常。
*
* 注意:当前线程被放到等待队列中时,只会放弃对当前对象的锁;任何其他出现在该线程中的其他对象的锁都会保留原样。
*
* 该方法只能被一个线程的对象的监视器的所有者调用。如何变成一个对象的监视器的所有者见notify方法(下面会讲到)。
*
* @param timeout 最大等待时间
* @throws IllegalArgumentException 如果timeout是负数,则抛出该异常
* @throws IllegalMonitorStateException 如果当前线程不是对象的监视器的拥有者,则抛出该异常
* @throws InterruptedException 如果线程wait过程中被打断,则抛出该异常,抛出此异常时清除当前线程的中断状态
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
* public final native void wait(long timeout) throws InterruptedException;
*/
接下来看看一个线程如何成为对象的监视器的拥有者,源码注释如下。
/**
* 唤醒在该对象监视器上等待的单个线程。如果有任何线程在等待该对象,则选择其中一个被唤醒。选择是任意的,由实现自行决定。线程
* 通过调用一个wait方法来等待对象的监视器。
*
* 被唤醒的线程将无法继续执行,直到当前线程放弃该对象上的锁。被唤醒的线程将以通常的方式与其他可能在此对象上积极竞争同步的线
* 程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程方面没有可靠的特权或缺点。
*
* 该线程只能被对象监视器的拥有者调用。线程成为对象监视器拥有者的方法有以下三种:
*
* 1.通过执行该对象的同步实例方法。
* 2.通过执行在对象上同步的同步语句的主体。
* 3.对于类对象,通过执行该类的同步静态方法。
*
*
* 一个线程在同一时间只能拥有一个对象监视器。
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notifyAll()
* @see java.lang.Object#wait()
*/
public final native void notify();