当前位置:   article > 正文

Java中的join方法原理详解

Java中的join方法原理详解

1.synchronized中的对象锁是线程的实例

我们可以使用同步语句块的方式对需要同步的代码进行包裹。

  1. Object obj = new Object();
  2. synchronized(obj){
  3.    obj.wait();    //线程在这里等待
  4. }

此时线程会在obj.wait()处等待,如果想继续执行,此时需要别的线程通过notify、notifyAll唤醒或者中断。但是如果obj是一个线程实例会怎么样呢?

如下面的例子:

  1. public class HighConcurrency {
  2. public static void main(String[] args) {
  3. try {
  4. Thread threadTest = new Thread(){
  5. public void run(){
  6. System.out.println("执行线程中方法");
  7. try {
  8. Thread.sleep(2000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. };
  14. threadTest.start();
  15. synchronized(threadTest){
  16.     threadTest.wait(); //当线程终止的时候,会调用线程自身的notifyAll()方法
  17. }
  18. System.out.println("执行到了这里");
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }

输出:

  1. 执行线程中方法
  2. 执行到了这里

首先开始线程threadTest,在主线程执行到threadTest.wait()时,主线程会等待,奇怪的是主线程并没有别的线程使用notify或notifyAll方法唤醒,竟然直接执行了后面的语句"执行了这里"。查阅发现如果synchronized获得对象锁是线程的实例时,此时比较特殊,当该线程终止的时候,会调用线程自身的notifyAll()方法,会通知所有等待在该线程对象上的线程

这有什么应用呢?

如下,我开启一个子线程计算一段数据,我希望计算完后最终结果后在主线程中再输出一段话(就当是对计算结果的反馈),此时可以采用类似上面的这段代码。

  1. class MyThread extends Thread {
  2. @Override
  3. public void run() {
  4. try {
  5. int secondValue = (int) (Math.random() * 3000);
  6. System.out.println(secondValue);
  7. Thread.sleep(secondValue);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. }
  13. public class HighConcurrency {
  14. public static void main(String[] args) {
  15. try {
  16. MyThread threadTest = new MyThread();
  17. threadTest.start();
  18. synchronized(threadTest){
  19. threadTest.wait(); //当线程终止的时候,会调用线程自身的notifyAll()方法
  20. }
  21. System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

输出:

  1. 1135
  2. 我想当threadTest对象执行完毕后我再执行,我做到了

过程不多解释,就是利用了线程实例做对象锁时,在线程执行完后,会调用线程自身的notifyAll()方法,此时主线程会接着执行,用处可以控制线程的执行顺序,例如我可以让子线程做计算,在子线程计算完后,在主线程中输出计算结果。

2.Join的原理

Java中的join方法也可以控制线程的执行顺序,上面的代码的功能使用join方法也可以很方便的实现:

  1. class MyThread extends Thread {
  2. @Override
  3. public void run() {
  4. try {
  5. int secondValue = (int) (Math.random() * 3000);
  6. System.out.println(secondValue);
  7. Thread.sleep(secondValue);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. }
  13. public class HighConcurrency {
  14. public static void main(String[] args) {
  15. try {
  16. MyThread threadTest = new MyThread();
  17. threadTest.start();
  18. threadTest.join();
  19. System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

输出的时功能一样。查看join的核心源码如下:

  1. public final void join() throws InterruptedException {
  2. join(0);
  3. }

 

  1. public final synchronized void join(long millis)
  2. 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. if (millis == 0) {             //如果时执行的join(0)
  9. while (isAlive()) { //如果线程是运行状态,就会执行下面的等待
  10. wait(0);
  11. }
  12. } else {                       //如果是执行的join(time)
  13. while (isAlive()) { //如果线程时运行状态
  14. long delay = millis - now;
  15. if (delay <= 0) {
  16. break;
  17. }
  18. wait(delay);                 //等待delay时间后自动返回继续执行
  19. now = System.currentTimeMillis() - base;
  20. }
  21. }
  22. }

其中核心代码如下:

  1. while (isAlive()) { //调用join方法线程是运行时状态
  2. wait(0); //进入等待
  3. }

isAlive()方法下面会做详细的介绍,先看wait(0),wait(0)是什么意思呢,查看下面wait()方法源码,其实wait()方法就是调用了wait(0)方法实现的,wait(0)就是让其一直等待。到这里会发现,其实join方法本质就是利用上面的线程实例作为对象锁的原理,当线程终止时,会调用线程自身的notifyAll()方法,通知所有等待在该线程对象上的线程的特征

  1. public final void wait() throws InterruptedException {
  2. wait(0);
  3. }

3.下面再说明一下while(isAlive)语句的作用:

有下面的一段简单代码:

代码实现:

  1. public class HighConcurrency {
  2. // 1.现在有T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
  3. final Thread t2 = new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. // 引用t1线程,等待t1线程执行完
  8. t1.join();
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. System.out.println("t2");
  13. }
  14. });
  15. Thread t3 = new Thread(new Runnable() {
  16. @Override
  17. public void run() {
  18. try {
  19. // 引用t2线程,等待t2线程执行完
  20. t2.join();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. System.out.println("t3");
  25. }
  26. });
  27. t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!
  28. t2.start();
  29. }
  30. }

程序输出:

  1. t2
  2. t3

首先执行了t3.start(),在t3线程里的run方法里执行了t2.join(),此时有两种情况,可能还没有执行t2.start(),t2处于初始状态,也有可能执行了t2.start(),t2处于运行时状态,所以看到这里就明白了,join源码中while(isAlive()),其实相当于while(this.isAlive())就相当于判断这里的t2是不是已经是不是运行状态(有没有调用start方法)。这么设计是为了防止t2线程没有运行,此时t3线程如果直接执行wait(0)方法,那么将会一直等待下去,造成代码卡死。

为了验证,代码改为:

  1.       t3.start();
  2. try {
  3. Thread.sleep(10);
  4. } catch (InterruptedException e) {
  5. e.printStackTrace();
  6. }
  7. t2.start();

此时的输出:

  1. t3
  2. t2

分析:将t2.start()和t3.start()之间的时间间隔变长,这样在t3线程中不会执行t2.join()时,保证了t2时处于初始状态还不是运行状态,此时while(isAlive())不成立,不会执行wait(0)方法,所以t3线程不会等待,会先输出t3。

然后再更改代码如下,保证在执行t2.join()时,t2.start()已经执行,t2已经处于运行状态:

  1. public class HighConcurrency {
  2. public static void main(String[] args) {
  3. final Thread t2 = new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. System.out.println("t2");
  7. }
  8. });
  9. Thread t3 = new Thread(new Runnable() {
  10. @Override
  11. public void run() {
  12. try {
  13. Thread.sleep(10); //保证此时t2已经是运行时状态了
  14. t2.join();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. System.out.println("t3");
  19. }
  20. });
  21. t3.start();
  22. t2.start();
  23. }
  24. }

此时的输出:

  1. t2
  2. t3

分析:在t3线程中执行t2.join()方法前先执行了sleep(10)方法,保证在执行t2.join()时,t2已经是运行时状态了,所以此时t3会执行wait(0)方法等待,直到t2先执行完,t3再继续执行,所以输出t2 t3。

 

参考:

http://uule.iteye.com/blog/1101994

https://www.nowcoder.com/questionTerminal/d8a288fc416c4d638dfb042e1be239fc?from=14pdf

http://www.cnblogs.com/paddix/p/5381958.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/294581
推荐阅读
相关标签
  

闽ICP备14008679号