A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 路文龙 中级黑马   /  2015-3-8 14:43  /  749 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

join方法从字面上的意思就是加入到一个线程中,这样就可以很好的进行线程之间的交互运行了,不多说先来看一下代码:
  1. package com.threadjoin.demo;
  2. public class ThreadJoin {
  3.         public static int a = 0;
  4.         public static void main(String[] args){
  5.                 Thread thread = new Thread(new Runnable(){
  6.                         @Override
  7.                         public void run(){
  8.                                 for(int i=0;i<5;i++)
  9.                                         a++;
  10.                         }
  11.                 });
  12.                 thread.start();
  13.                 /*try {
  14.                         thread.join();
  15.                 } catch (InterruptedException e) {
  16.                         e.printStackTrace();
  17.                 }*/
  18.                 System.out.println(a);
  19.         }
  20. }
  21.         join(0);  
  22. }
复制代码
  运行代码,貌似永远都看不到a的值是5,而每次都是0,原因很简单的,因为在thread中的run方法中进行a的增值操作,这些可能都是需要时间的,但是此时main线程中的System.out.println方法已经执行了,所以很难看到a的值是5,为了看到a的值是5,我的一个思路就是等thread运行结束之后,我们采取执行System.out.println就可以了,这时候join方法的作用就显现出来的,我们把上面的注释代码删除注释,然后运行,不管运行多少次,输出的结果都是5,从这个例子中我们就可以看到join方法的作用,它能够调节各个线程之间的运行顺序,从而可以实现同步。为了更好的了解join的运行原理我们只有看他的源码了:
  1. public final void join() throws InterruptedException {  
  2.         join(0);  
  3. }
复制代码
我们在跟踪到join(0)方法中:
  1. //方法是个同步的,而且会抛出InterruptedException异常  
  2. public final synchronized void join(long millis) throws InterruptedException {  
  3.     long base = System.currentTimeMillis();  
  4.     long now = 0;  
  5.   
  6.     if (millis < 0) {  
  7.         throw new IllegalArgumentException("timeout value is negative");  
  8.     }  
  9.     //我们可以看到这里使用了while循环做判断的,然后调用wait方法的,所以说join方法的执行是完全通过wait方法实现的  
  10.     //等待时间为0的时候,就是无限等待,直到线程死亡了(即线程执行完了)  
  11.     if (millis == 0) {  
  12.         //如果当前线程还存活的话,就等待  
  13.         while (isAlive()) {  
  14.             //调用该线程的join方法的线程拿到锁之后进行等待,直到线程执行结束(这个例子就是main线程)  
  15.             wait(0);  
  16.         }  
  17.     } else {  
  18.         //如果是等待的特定时间的话  
  19.         while (isAlive()) {  
  20.             long delay = millis - now;  
  21.             if (delay <= 0) {  
  22.                 break;  
  23.             }  
  24.             wait(delay);  
  25.             now = System.currentTimeMillis() - base;  
  26.         }  
  27.     }  
  28. }
复制代码
从代码中我们可以看到join方法是个同步的,这个我们后面会做个例子,然后进入到方法中我们可以看到,有两种情况,一种是等待时间是0的,其实就等同无线等待,直到线程执行结束了,还有一种就是要等待的是一定的时间,原理都是一样的,

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


  1.     package com.threadjoin.demo;  
  2.       
  3.     /**
  4.      *  
  5.     其实Join方法实现是通过wait(小提示:Object 提供的方法)。
  6.      当main线程调用t.join时候,main线程会获得线程对象t的锁(wait意味着拿到该对象的锁),
  7.      调用该对象的wait(等待时间),直到该对象唤醒main线程,比如退出后。
  8.     这就意味着main 线程调用t.join时,
  9.     必须能够拿到线程t对象的锁,如果拿不到它是无法wait的,刚开的例子t.join(1000)不是说明了main线程等待1秒,
  10.     如果在它等待之前,其他线程获取了t对象的锁,它等待时间可不就是1秒了
  11.      * @author weijiang204321
  12.      *
  13.      */  
  14.     public class ThreadJoinTest {  
  15.       
  16.         public static void main(String[] args) {  
  17.             Thread t = new Thread(new RunnableImpl());  
  18.             new ThreadTest(t).start();  
  19.             t.start();  
  20.             try {  
  21.                 t.join(1000);  
  22.                 System.out.println("joinFinish");  
  23.             } catch (InterruptedException e) {  
  24.                 e.printStackTrace();  
  25.            
  26.             }  
  27.         }  
  28.     }  
  29.       
  30.     class RunnableImpl implements Runnable {  
  31.         @Override  
  32.         public void run() {  
  33.                 try {  
  34.                     System.out.println("Begin sleep");  
  35.                     Thread.sleep(1000);  
  36.                     System.out.println("End sleep");  
  37.                 } catch (InterruptedException e) {  
  38.                     e.printStackTrace();  
  39.                 }  
  40.             }  
  41.     }  
  42.       
  43.     class ThreadTest extends Thread {  
  44.         Thread thread;  
  45.         public ThreadTest(Thread thread) {  
  46.             this.thread = thread;  
  47.         }  
  48.       
  49.         @Override  
  50.         public void run() {  
  51.             holdThreadLock();  
  52.         }  
  53.       
  54.         public void holdThreadLock() {  
  55.             //用当前的线程当做lock  
  56.             synchronized (thread) {  
  57.                 System.out.println("getObjectLock");  
  58.                 try {  
  59.                     Thread.sleep(9*1000);  
  60.                 } catch (InterruptedException ex) {  
  61.                  ex.printStackTrace();  
  62.                 }  
  63.                 System.out.println("ReleaseObjectLock");  
  64.             }  
  65.       
  66.         }  
  67.     }  
复制代码
在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。




评分

参与人数 1技术分 +1 收起 理由
lwj123 + 1 淡定

查看全部评分

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马