Thread join方法细探
2017-01-20 00:00
155 查看
join方法的实现
首先join是Thread的成员函数,假设thread是代表一个线程实例,那么thread.join()的意思就是让当前线程(调用thread.join的线程)等待thread的线程死亡之后才能再次获得执行的机会。thread.join(timeout)的意思是让当前线程(调用thread.join(timeout)的线程)执行timeout毫秒之后才能再一次获得执行机会。因为join()调用的是join(0),所以我们来看一下Thread#join(long timeout)代码:
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"); } //Flag if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
注意看一下Flag标注的代码,isAlive()也是Thread的成员函数,通过isAlive()判断thread线程是否还活在。当前线程还活在就调用wait(0),wait(0)是Object的成员函数,就是让当前线程等待notify为止。wait(0)的原理是通过锁的获取与释放来实现的。要调用wait(0)一定持有锁。我们看join方法是synchronized修饰的,所以wait(0)获取的锁是Thread线程实例thread的this锁。
现在问题就变为调用thread.join()的线程获取了thread的this锁,然后通过wait释放锁等待重新获取锁。
现在问题来了,我们在join方法中没有看到任何notify,notifyAll操作,那么调用thread.join()的线程什么时候重新获取锁呢?答案是JVM在线程结束的时候会调用void JavaThread::exit(bool destroy_vm, ExitType exit_type)(在虚拟机中实现的)方法,这个方法中会执行notifyAll操作。所以调用thread.join()的线程会等到thread线程死了之后才能执行。
join的应用
那么join的应用场景呢?考虑下面的代码:public class JoinTest { static int sum = 0; static class ThreadA extends Thread { public ThreadA(){} public ThreadA(String name) { super(name); } @Override public void run() { System.out.println(Thread.currentThread().getName()); for(int i=1;i<=100;i++) sum+=i; } } public static void main(String[] args) { ThreadA threadA = new ThreadA("threadA"); threadA.start(); System.out.println(sum); } }
代码本意是想执行threadA线程执行从1到100的叠加操作,然后输出结果。但是输出的结果一般情况下是被正确的,多数情况下是0。这是什么原因呢?是因为threadA.start()只是告诉jvm线程可以执行,但是并不一定马上执行,threadA.start()是在main线程中执行的,然后main线程接着执行 System.out.println(sum),因为大多数时候还没有开始执行threadA线程,所以打印的sum为0。
当然你可以通过Thread.sleep(timeout)来让main线程等待ThreadA线程执行,但是main线程应该等待多久呢?显然这样的实现是不够优雅的。
当然你可以通过CountDownLatch方式来实现:
import java.util.concurrent.CountDownLatch; public class JoinTest { private static CountDownLatch latch = new CountDownLatch(1); static int sum = 0; static class ThreadA extends Thread { public ThreadA(){} public ThreadA(String name) { super(name); } @Override public void run() { System.out.println(Thread.currentThread().getName()); for(int i=1;i<=100;i++) sum+=i; latch.countDown(); } } public static void main(String[] args) { ThreadA threadA = new ThreadA("threadA"); threadA.start(); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(sum); } }
当然对于这种只是简单的一个线程等另一个线程结束,那么前面结束的join方法就是一个不错的选择:
public class JoinTest { static int sum = 0; static class ThreadA extends Thread { @Override public void run() { for(int i=1;i<=100;i++) sum+=i; } } public static void main(String[] args) { ThreadA threadA = new ThreadA(); threadA.start(); try { threadA.join(0); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(sum); } }
总结
join方法是Thread类的synchronized实例方法,所以拿到Thread实例的线程调用Thread实例的锁,然后释放Thread实例的锁,然后等待其他线程通知可以重新获取Thread实例的锁,但是没有线程会notify通过join的wait操作,所以只有等Thread实例线程本身死亡的时候jvm调用notifyAll来唤醒在Thread线程上执行join操作的线程。注意把Thread实例和线程区分清楚。相关文章推荐
- Thread.Join()方法的理解
- 多线程:Thread类的Join()方法
- Java中的线程Thread方法之---join()
- 多线程:Thread类的Join()方法
- C#中Thread类中Join方法的理解(转载)
- 关于Thread.join( )方法
- Thread的Abort和Join方法总结
- [Thread] 執行緒的順序啟動 - Thread.Join方法
- Thread的join方法使用
- Thread.join方法个人理解
- Thread.join()不好用的解决方法
- Java Thread Join方法
- Thread的join方法使用解析
- 利用Thread类的join方法实现线程同步
- java中Thread类的join()方法
- C#中的Thread类的Join()方法
- C# 线程Thread的Join()方法
- Thread类的join()方法、Using、SqlCommand、is和as、Session、ViewState、Cookie
- Thread 提供的让一个线程等待另一个线程完成的方法:join()方法
- Thread类的join方法学习笔记