Java中的线程Thread方法之---join()

2014-11-24 02:25:26 · 作者: · 浏览: 0

上一篇我们说到了Thread中的stop方法,这一篇我们再来看一下方法join的使用,那么方法Join是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答。


join方法从字面上的意思就是加入到一个线程中,这样就可以很好的进行线程之间的交互运行了,不多说先来看一下代码:

package com.threadjoin.demo;

public class ThreadJoin {
	
	public static int a = 0;
	
	public static void main(String[] args){
		Thread thread = new Thread(new Runnable(){
			@Override
			public void run(){
				for(int i=0;i<5;i++)
					a++;
			}
		});
		thread.start();
		/*try {
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		System.out.println(a);
		
	}
	
}

运行代码,貌似永远都看不到a的值是5,而每次都是0,原因很简单的,因为在thread中的run方法中进行a的增值操作,这些可能都是需要时间的,但是此时main线程中的System.out.println方法已经执行了,所以很难看到a的值是5,为了看到a的值是5,我的一个思路就是等thread运行结束之后,我们采取执行System.out.println就可以了,这时候join方法的作用就显现出来的,我们把上面的注释代码删除注释,然后运行,不管运行多少次,输出的结果都是5,从这个例子中我们就可以看到join方法的作用,它能够调节各个线程之间的运行顺序,从而可以实现同步。为了更好的了解join的运行原理我们只有看他的 源码了:

[java] view plaincopy 在CODE上查看代码片 派生到我的代码片
  1. public final void join() throws InterruptedException {
  2. join(0);
  3. } 我们在跟踪到join(0)方法中:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. //方法是个同步的,而且会抛出InterruptedException异常
    2. public final synchronized void join(long millis) throws InterruptedException {
    3. long base = System.currentTimeMillis();
    4. long now = 0;
    5. if (millis < 0) {
    6. throw new IllegalArgumentException("timeout value is negative");
    7. }
    8. //我们可以看到这里使用了while循环做判断的,然后调用wait方法的,所以说join方法的执行是完全通过wait方法实现的
    9. //等待时间为0的时候,就是无限等待,直到线程死亡了(即线程执行完了)
    10. if (millis == 0) {
    11. //如果当前线程还存活的话,就等待
    12. while (isAlive()) {
    13. //调用该线程的join方法的线程拿到锁之后进行等待,直到线程执行结束(这个例子就是main线程)
    14. wait(0);
    15. }
    16. } else {
    17. //如果是等待的特定时间的话
    18. while (isAlive()) {
    19. long delay = millis - now;
    20. if (delay <= 0) {
    21. break;
    22. }
    23. wait(delay);
    24. now = System.currentTimeMillis() - base;
    25. }
    26. }
    27. } 从代码中我们可以看到join方法是个同步的,这个我们后面会做个例子,然后进入到方法中我们可以看到,有两种情况,一种是等待时间是0的,其实就等同无线等待,直到线程执行结束了,还有一种就是要等待的是一定的时间,原理都是一样的,


      看完源码之后我们在看一一个例子:

      [java] view plaincopy在CODE上查看代码片派生到我的代码片
      1. package com.threadjoin.demo;
      2. /**
      3. *
      4. 其实Join方法实现是通过wait(小提示:Object 提供的方法)。
      5. 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait意味着拿到该对象的锁),
      6. 调用该对象的wait(等待时间),直到该对象唤醒main线程,比如退出后。
      7. 这就意味着main 线程调用t.join时,
      8. 必须能够拿到线程t对象的锁,如果拿不到它是无法wait的,刚开的例子t.join(1000)不是说明了main线程等待1秒,
      9. 如果在它等待之前,其他线程获取了t对象的锁,它等待时间可不就是1秒了
      10. * @author weijiang204321
      11. *
      12. */
      13. public class ThreadJoinTest {
      14. public static void main(String[] args) {
      15. Thread t = new Thread(new RunnableImpl());
      16. new ThreadTest(t).start();
      17. t.start();
      18. try {
      19. t.join(1000);
      20. System.out.println("joinFinish");
      21. } catch (InterruptedException e) {
      22. e.printStackTrace();
      23. }
      24. }
      25. }
      26. class RunnableImpl implements Runnable {
      27. @Override
      28. public void run() {
      29. try {
      30. System.out.println("Begin sleep");
      31. Thread.sleep(1000);
      32. System.out.println("End sleep");
      33. } catch (InterruptedException e) {
      34. e.printStackTrace();
      35. }
      36. }
      37. }
      38. class ThreadTest extends Thread {
      39. Thread thread;
      40. public ThreadTest(Thread thread) {
      41. this.thread = thread;
      42. }
      43. @Override
      44. public void run() {
      45. holdThreadLock();
      46. }
      47. public void holdThreadLock() {
      48. //用当前的线程当做lock
      49. synchronized (thread) {
      50. System.out.println("getObjectLock");
      51. try {
      52. Thread.sleep(9*1000);
      53. } catch (InterruptedException ex) {
      54. ex.printStackTrace();
      55. }
      56. System.out.println("ReleaseObjectLock");
      57. }
      58. }
      59. } 在main方法中 通过new ThreadTest(t).start()实例化 ThreadTest 线程对象, 它 通过 synchronized (thread) ,获取线程对象t的锁,并sleep(9*1000)后释放,因为我们上面看到了join方法是个同步的,而且同步锁是当前的线程对象,因为ThreadTest先运行的,首先拿到了线程t对象的锁,所以join方法还没有拿到锁,所以要等待。这就意味着,即使main方法t.join(1000)等待一秒钟,它必须等待ThreadTest 线程释放t锁后才能进入wait方法中,它实际等待时间是9000+1000ms=10s。