在线程的常见方法一节中,已经接触过join()方法的使用。html
在不少状况下,主线程建立并启动子线程,若是子线程中要进行大量的耗时运算,主线程将早于子线程结束。这时,若是主线程想等子线程执行完成才结束,好比子线程处理一个数据,主线程想要得到这个数据中的值,就要用到join()方法了。方法join()的做用是等待线程对象销毁。ide
join方法的主要做用就是同步,它可使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。测试
join方法中若是传入参数,则表示这样的意思:若是A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒事后,A、B线程并行执行。须要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。(其实join()中调用的是join(0))this
join方法必须在线程start方法调用以后调用才有意义。这个也很容易理解:若是一个线程都没有start,那它也就没法同步了。spa
源码以下: 方法join(long)的功能在内部是使用wait(long)来实现的,因此join(long)方法具备释放锁的特色。线程
public final void join() throws InterruptedException { join(0); } 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; } } }
package cn.qlq.thread.nine; /** * 线程类join()使用方法 * * @author Administrator * */ public class Demo1 extends Thread { /** * 更改线程名字 * * @param threadName */ public Demo1(String threadName) { this.setName(threadName); } @Override public void run() { for (int i = 0; i < 2; i++) { try { Thread.sleep(1 * 500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-----" + i); } } public static void main(String[] args) { Demo1 t1 = new Demo1("t1"); Demo1 t2 = new Demo1("t2"); Demo1 t3 = new Demo1("t3"); t1.start(); /** * join的意思是使得放弃当前线程的执行,并返回对应的线程,例以下面代码的意思就是: * 程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕 * 因此结果是t1线程执行完后,才到主线程执行,至关于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会 */ try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } if (t2.isAlive()) { System.out.println("t2 is alive"); } else { System.out.println("t2 is not alive"); } t2.start(); t3.start(); } }
结果:3d
t1-----0
t1-----1
t2 is not alive
t3-----0
t2-----0
t2-----1
t3-----1code
方法x.join()的做用是使所属线程x 正常执行run()中的方法,而使得调用x.join()的线程处于无限期阻塞状态,等待x线程销毁后再继续执行线程z后面的代码。htm
方法join()具备使线程排队运行的做用,有些相似于同步的运行效果。join()与synchronized的区别是:join在内部调用wait()方法进行等待,而synchronized关键字使用的是"对象监视器"原理做为同步。对象
在join()过程当中,若是当前线程被中断,则当前线程出现异常。(注意是调用thread.join()的线程被中断才会进入异常,好比a线程调用b.join(),a中断会报异常而b中断不会异常)
以下:threadB中启动threadA,而且调用其方法等待threadA完成,此时向threadB发出中断信号,会进入中断异常代码。
package cn.qlq.thread.nine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 线程类join()使用方法--join中中断 * * @author Administrator * */ public class Demo2 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class); public static void main(String[] args) throws InterruptedException { final Thread threadA = new Thread(new Runnable() { @Override public void run() { LOGGER.info("threadA run"); while (true) { } } }, "threadA"); Thread threadB = new Thread(new Runnable() { @Override public void run() { LOGGER.info("threadB run"); threadA.start(); try { threadA.join(); } catch (InterruptedException e) { LOGGER.error("join error ,threadName - > {}", Thread.currentThread().getName(), e); } } }, "threadB"); threadB.start(); // 向threadB发出中断信号 Thread.sleep(1 * 1000); threadB.interrupt(); } }
结果:
上面虽然进入异常代码块,可是线程仍然未中止 (由于threadA并无抛出异常,因此仍然在存活),咱们用jvisualVM查看线程:
方法join(long)是设定等待的时间。实际join()方法中调用的是join(0),当参数是0的时候表示无限期等待。
package cn.qlq.thread.nine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 线程类join()使用方法--join中中断 * * @author Administrator * */ public class Demo3 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class); public static void main(String[] args) throws InterruptedException { final Thread threadA = new Thread(new Runnable() { @Override public void run() { LOGGER.info("threadA run"); while (true) { } } }, "threadA"); Thread threadB = new Thread(new Runnable() { @Override public void run() { LOGGER.info("threadB run"); threadA.start(); try { threadA.join(2 * 1000); } catch (InterruptedException e) { LOGGER.error("join error ,threadName - > {}", Thread.currentThread().getName(), e); } LOGGER.info("threadB end"); } }, "threadB"); threadB.start(); } }
结果:(threadB线程等待threadA线程2秒钟以后两个线程开始并行运行)
方法join(long)的功能在内部是使用wait(long)来实现的,因此join(long)方法具备释放锁的特色。
方法join(long)的源码以下:
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; } } }
从源码能够看出,调用join(long)方法以后内部调用了wait()方法,所以会释放该对象锁。
package cn.qlq.thread.nine; import org.slf4j.Logger; import org.slf4j.LoggerFactory;/** * join(long)释放锁 * * @author Administrator * */ public class Demo4 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo4.class); public static void main(String[] args) throws InterruptedException { LOGGER.info("main start"); final Demo4 demo4 = new Demo4(); // 启动demo4线程而且占用锁以后调用join(long)方法 new Thread(new Runnable() { @Override public void run() { try { synchronized (demo4) { LOGGER.info("进入同步代码块,threadName ->{} 占有 demo4 的锁", Thread.currentThread().getName()); demo4.start(); demo4.join(4 * 1000); LOGGER.info("退出同步代码块,threadName ->{}", Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } }, "threadA").start(); // 休眠2秒钟,调用对象的同步方法 Thread.currentThread().sleep(2 * 1000); demo4.test2(); } @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void test2() { LOGGER.info("进入test2方法,占有锁,threadname->{}", currentThread().getName()); } }
结果: (在休眠2秒钟后调用对象的同步方法能进入方法则证实join方法释放锁;并且在退出同步代码块以前打印了test信息则说明test2占用锁成功)
17:57:02 [cn.qlq.thread.nine.Demo4]-[INFO] main start 17:57:02 [cn.qlq.thread.nine.Demo4]-[INFO] 进入同步代码块,threadName ->threadA 占有 demo4 的锁 17:57:04 [cn.qlq.thread.nine.Demo4]-[INFO] 进入test2方法,占有锁,threadname->main 17:57:06 [cn.qlq.thread.nine.Demo4]-[INFO] 退出同步代码块,threadName ->threadA
(2)测试sleep(long)不会释放锁
package cn.qlq.thread.nine; import org.slf4j.Logger; import org.slf4j.LoggerFactory;/** * sleep(long)不会释放锁 * * @author Administrator * */ public class Demo5 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); public static void main(String[] args) throws InterruptedException { LOGGER.info("main start"); final Demo5 demo4 = new Demo5(); // 启动demo4线程而且占用锁以后调用join(long)方法 new Thread(new Runnable() { @Override public void run() { try { synchronized (demo4) { LOGGER.info("进入同步代码块,threadName ->{} 占有 demo4 的锁", Thread.currentThread().getName()); demo4.start(); demo4.sleep(4 * 1000); LOGGER.info("退出同步代码块,threadName ->{}", Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } }, "threadA").start(); // 休眠2秒钟,调用对象的同步方法 Thread.currentThread().sleep(2 * 1000); demo4.test2(); } @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void test2() { LOGGER.info("进入test2方法,占有锁,threadname->{}", currentThread().getName()); } }
结果:(退出代码块才进入test2方法,证实sleep(long)没有释放锁)
17:59:30 [cn.qlq.thread.nine.Demo5]-[INFO] main start17:59:30 [cn.qlq.thread.nine.Demo5]-[INFO] 进入同步代码块,threadName ->threadA 占有 demo4 的锁17:59:34 [cn.qlq.thread.nine.Demo5]-[INFO] 退出同步代码块,threadName ->threadA17:59:34 [cn.qlq.thread.nine.Demo5]-[INFO] 进入test2方法,占有锁,threadname->main