public final void join() throws InterruptedException
等待该线程终止。
public final void join(long millis) throws InterruptedException
等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。
public final void join(long millis, int nanos) throws InterruptedException
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
package basic.c_join; public class TestJoinA { public static void main(String[] args) { Thread t = new Thread(new TestRunnableImpl()); t.start(); try { t.join(1000); // 主线程只等1秒,不管子线程什么时候结束 System.out.println("joinFinish"); } catch (InterruptedException e) { e.printStackTrace(); } } } class TestRunnableImpl implements Runnable { public void run() { try { System.out.println("Begin sleep"); // 如果改成sleep(2000), 那么主线程就会先结束 Thread.sleep(1000); System.out.println("End sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } }
其实Join 方法实现是通过wait (小提示:Object 提供的方法)。 当main 线程调用t.join 时候,main 线程会获得线程对象t 的锁 (wait 意味着拿到该对象的锁), 调用该对象的wait( 等待时间) ,直到该对象唤醒main 线程,比如退出后。
这就意味着main 线程调用t.join 时,必须能够拿到线程t 对象的锁 ,如果拿不到它是无法wait 的,刚开的例子t.join(1000) 不是说明了main 线程等待1 秒,如果在它等待之前,其他线程获取了t 对象的锁,它等待时间可不就是1 毫秒了。
package basic.c_join; import org.apache.log4j.Logger; public class TestJoinB { private static Logger logger = Logger.getLogger(TestJoinB.class); public static void main(String[] args) { Thread t = new Thread(new RunnableImpl()); new ThreadTest(t).start(); // 这个线程会持有锁 t.start(); try { // 因为线程对象t的锁已经被其他线程获得, 要 logger.debug("beforejoin"); t.join(); logger.debug("joinFinish"); } catch (InterruptedException e) { e.printStackTrace(); } } } class ThreadTest extends Thread { private static Logger logger = Logger.getLogger(ThreadTest.class); Thread thread; public ThreadTest(Thread thread) { this.thread = thread; } @Override public void run() { logger.debug("run1 before"); holdThreadLock(); logger.debug("run1 end"); } public void holdThreadLock() { synchronized (thread) { logger.debug("getObjectLock"); try { Thread.sleep(9000); } catch (InterruptedException ex) { ex.printStackTrace(); } logger.debug("ReleaseObjectLock"); } } } class RunnableImpl implements Runnable { private static Logger logger = Logger.getLogger(RunnableImpl.class); public void run() { logger.debug("run2 before"); try { logger.debug("Begin sleep"); Thread.sleep(2000); logger.debug("End sleep"); } catch (InterruptedException e) { e.printStackTrace(); } logger.debug("run2 end"); } }