join:异步阻塞之闷葫芦
阻塞模式实现泡茶实例首先从基础的多线程join合并实验入手.join操作的原理是阻塞当前线程,直到待合并的目标线程执行完成.
线程的合并流程
Java中线程的合并流程是:假设线程A调用线程B的join()方法去合并B线程,那么线程A进入阻塞状态,直到线程B执行完成.
泡茶例子中,主线程通过分别调用烧水线程和清洗线程的join()方法,等待烧水线程和清洗线程完成,然后主线程执行泡茶操作.流程参考图如下.
通过join()实现异步泡茶喝
通过join实现一个异步阻塞版本,参考如下.
public class JoinDemo {
public static final int SLEEP_GAP = 500;
public static String getCurrentThreadName() {
return Thread.currentThread().getName();
}
static class HotWaterThread extends Thread {
public HotWaterThread() {
super("烧水--Thread");
}
@Override
public void run() {
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
Thread.sleep(SLEEP_GAP);
System.out.println("水开了");
} catch (InterruptedException e) {
System.out.println("烧水失败");
}
System.out.println("运行结束");
}
}
static class WashThread extends Thread {
public WashThread() {
super("清洗--Thread");
}
@Override
public void run() {
try {
System.out.println("洗茶壶");
System.out.println("洗茶杯");
System.out.println("拿茶叶");
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了");
} catch (InterruptedException e) {
System.out.println("洗茶壶茶杯失败");
}
System.out.println("运行结束");
}
}
public static void main(String[] args) {
WashThread washThread = new WashThread();
HotWaterThread hotWaterThread = new HotWaterThread();
washThread.start();
hotWaterThread.start();
try {
washThread.join();
hotWaterThread.join();
Thread.currentThread().setName("主线程");
System.out.println("泡茶喝");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getCurrentThreadName() + "运行结束");
}
}
程序中有三个线程:主线程main 烧水线程HotWaterThread 清洗线程WashThread,主线程调用了这两个线程的join方法进行合并.
Join()方法详解
join方法应用场景如下:
A线程调用B线程的join方法,等待B线程执行完成,在B线程没有完成之前,A线程阻塞.
Join()方法有三个重载版本:
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
A线程等待B线程执行结束后,A线程重新开始执行.
/**
* Counts the number of stack frames in this thread. The thread must
* be suspended.
*
* @return the number of stack frames in this thread.
* @exception IllegalThreadStateException if this thread is not
* suspended.
* @deprecated The definition of this call depends on {@link #suspend},
* which is deprecated. Further, the results of this call
* were never well-defined.
*/
@Deprecated
public native int countStackFrames();
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
A线程等待B线程执行一段时间,最长时间为millis 毫秒,超过后就会A线程重新执行.
/**
* Waits at most {@code millis} milliseconds plus
* {@code nanos} nanoseconds for this thread to die.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to wait
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value
* of {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
A线程等待B线程执行一段时间,最长时间为millis 毫秒加nanos纳秒,超过后就会A线程重新执行.
容易混淆的几点.
join()是实例方法,不是静态方法.
调用join方法时,不是thread所指向的目标线程阻塞,而是当前线程被阻塞.
只有等待的thread执行完成或者超时,当前线程才能启动执行.
总结下,join方法通过上面的源码可以看出,是不断通过检查线程是否存存活,来进行阻塞.直到被唤醒才会解除阻塞.而且join方法不能拿到返回结果,缺少很多的灵活性.所以join更多的停留在Demo演示上.
坚持读书久了,会发现自己变得温文尔雅.技术坚持久了,理解也会越来越深.
多给自己一点坚持.云淡风轻 温文尔雅是由内而外的.