Java线程的join()方法浅析

1.join()的作用

调用某个线程的join()方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。

2.放上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);
    }
/**
     * 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;
            }
        }
    }
/**
     * 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);
    }

3.分析

1)join()共有三个重载版本,join()和join(long millis, int nanos)最后都调用了join(long millis)。

2)带参数的 join() 都是 synchronized方法。

3)join() 调用了 join(0),从源码可以看到 join(0) 不断检查当前线程(join() 所属的线程实例,非调用线程)是否是 Active。如果被调用线程的对象生成了,但是未启动,调用它的join()是没有作用的,将继续向下执行。

4)当一个线程调用另一个线程的join()时,会先获取到被调用线程的锁,然后调用wait方法释放掉被调用线程的锁。

5)join()具有使线程排队运行的作用,有些类似同步的运行效果,join()与synchronized的区别是:join()在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。

6)join() 和 sleep() 一样,都可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会释放锁,而 sleep() 会一直持有锁,如下所示:

类ThreadB.java代码如下:

package com.smile13.joinandsleep;

/**
 * Created by eric on 2018/7/31.
 */
public class ThreadB extends Thread {

	@Override
	public void run() {
		try {
			System.out.println(" b run begin thimer = " + System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println(" b run end thimer = " + System.currentTimeMillis());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void bService() {
		System.out.println("打印了 bService timer = " + System.currentTimeMillis());
	}
}

类ThreadA.java代码如下:

package com.smile13.joinandsleep;

/**
 * Created by eric on 2018/7/31.
 */
public class ThreadA extends Thread {

	private ThreadB b;

	public ThreadA(ThreadB b) {
		super();
		this.b = b;
	}

	@Override
	public void run() {
		try {
			synchronized (b) {
				b.start();
				Thread.sleep(6000); // 不释放锁
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

类ThreadC.java代码如下:

package com.smile13.joinandsleep;

/**
 * Created by eric on 2018/7/31.
 */
public class ThreadC extends Thread {

	private ThreadB threadB;

	public ThreadC(ThreadB threadB) {
		super();
		this.threadB = threadB;
	}

	@Override
	public void run() {
		threadB.bService();
	}
}

类Run.java代码如下:

package com.smile13.joinandsleep;

/**
 * Created by eric on 2018/7/31.
 */
public class Run {

	public static void main(String[] args) {

		try {
			ThreadB b = new ThreadB();
			ThreadA a = new ThreadA(b);
			a.start();

			Thread.sleep(1000);

			ThreadC c = new ThreadC(b);
			c.start();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

程序Run.java运行后的效果如下所示:

 b run begin thimer = 1533050891103
 b run end thimer = 1533050896103
打印了 bService timer = 1533050897103

说明:由于线程ThreadA使用Thread.sleep(long millis)方法一直持有ThreadB对象的锁,时间达到6秒,所以线程ThreadC只有在ThreadA时间达到6秒后释放ThreadB对象的锁时,才能调用ThreadB中的同步方法bService(),上面的实验证明Thread.sleep(long millis)不会释放锁

下面修改ThreadA的代码,验证join()方法会释放锁。

修改ThreadA.java类的代码如下:

package com.smile13.joinandsleep;

/**
 * Created by eric on 2018/7/31.
 */
public class ThreadA extends Thread {

	private ThreadB b;

	public ThreadA(ThreadB b) {
		super();
		this.b = b;
	}

	@Override
	public void run() {
		try {
			synchronized (b) {
				b.start();
				b.join(); // 说明join()释放锁
				System.out.println("线程b执行完成,线程a继续执行...");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

程序Run.java运行后的效果如下所示:

b run begin thimer = 1533052328339
打印了 bService timer = 1533052329340
 b run end thimer = 1533052333340
线程b执行完成,线程a继续执行...

说明:由于线程ThreadA释放了ThreadB的锁,所以线程ThreadC可以调用ThreadB的同步方法bService(),此实验说明join()方法具有释放锁的特点。

4.未完待续....

猜你喜欢

转载自blog.csdn.net/smiles13/article/details/81322357