Java中的join方法原理详解

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

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

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

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

如下面的例子:

public class HighConcurrency {

	public static void main(String[] args) {
		try {
			Thread threadTest = new Thread(){
				public void run(){
					System.out.println("执行线程中方法");
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			threadTest.start();
			synchronized(threadTest){
			    threadTest.wait();		//当线程终止的时候,会调用线程自身的notifyAll()方法
			}
			System.out.println("执行到了这里");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

输出:

执行线程中方法
执行到了这里

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

这有什么应用呢?

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

class MyThread extends Thread {
	@Override
	public void run() {
		try {
			int secondValue = (int) (Math.random() * 3000);
			System.out.println(secondValue);
			Thread.sleep(secondValue);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class HighConcurrency {

	public static void main(String[] args) {
		try {
			MyThread threadTest = new MyThread();
			threadTest.start();
			synchronized(threadTest){
				threadTest.wait();	//当线程终止的时候,会调用线程自身的notifyAll()方法
			}
			System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

输出:

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

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

2.Join的原理

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

class MyThread extends Thread {
	@Override
	public void run() {
		try {
			int secondValue = (int) (Math.random() * 3000);
			System.out.println(secondValue);
			Thread.sleep(secondValue);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class HighConcurrency {

	public static void main(String[] args) {
		try {
			MyThread threadTest = new MyThread();
			threadTest.start();
			threadTest.join();
			System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

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

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) {             //如果时执行的join(0)
            while (isAlive()) {        //如果线程是运行状态,就会执行下面的等待
                wait(0);               
            }
        } else {                       //如果是执行的join(time)
            while (isAlive()) {               //如果线程时运行状态
                long delay = millis - now;    
                if (delay <= 0) {
                    break;
                }
                wait(delay);                 //等待delay时间后自动返回继续执行
                now = System.currentTimeMillis() - base;
            }
        }
 }

其中核心代码如下:

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

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

public final void wait() throws InterruptedException {
    wait(0);
}

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

有下面的一段简单代码:

代码实现:

public class HighConcurrency {
	 
    // 1.现在有T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 引用t1线程,等待t1线程执行完
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2");
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 引用t2线程,等待t2线程执行完
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t3");
            }
        });
        t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!
        t2.start();
    }
}

程序输出:

t2
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)方法,那么将会一直等待下去,造成代码卡死。

为了验证,代码改为:

        t3.start();
        try {
		Thread.sleep(10);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
        t2.start();

此时的输出:

t3
t2

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

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

public class HighConcurrency {
	
    public static void main(String[] args) {
 
        final Thread t2 = new Thread(new Runnable() {
 
            @Override
            public void run() {         
                System.out.println("t2");
            }
        });
        Thread t3 = new Thread(new Runnable() {
 
            @Override
            public void run() {
                try {
                	Thread.sleep(10);    //保证此时t2已经是运行时状态了
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t3");
            }
        });
        t3.start();
        t2.start();
    }
}

此时的输出:

t2
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

  • 25
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
### 回答1: 在 Java join 方法是用来等待一个线程终止的。你可以将 join 方法理解为“让我等着你结束,然后我再执行”。 你可以在一个线程对象上调用 join 方法,来等待这个线程终止。比如: ``` Thread t = new Thread(new Runnable() { public void run() { // 线程执行的任务 } }); t.start(); t.join(); // 等待 t 线程终止 ``` join 方法还有一个重载版本,允许你在等待一段时间后放弃等待: ``` t.join(1000); // 等待 t 线程终止,但最多等待 1000 毫秒 ``` 如果在指定的时间内 t 线程还没有终止,那么 join 方法就会立即返回。 另外,如果你不想等待线程 t 终止,你可以调用 t.isAlive() 方法来检查线程 t 是否还存活,如果存活就不用调用 join 方法了。 ### 回答2: 在Javajoin方法是Thread类提供的一个方法,它用于将一个线程加入到当前线程,等待该线程执行完毕后再继续执行。 join方法的调用者是当前线程,它用于管理其他线程的执行顺序。当调用某个线程join方法时,当前线程将会进入阻塞状态,直到给定的线程执行完毕后才会继续执行。 join方法有两种重载方式: 1. join(): 这种方式没有参数,调用该方法线程将会等待该线程执行完毕。 2. join(long millis): 这种方式接受一个参数,表示等待的时间(单位为毫秒)。如果在指定的时间内线程没有执行完毕,当前线程将会继续执行。 使用join方法可以方便地控制多个线程的执行顺序。例如,如果有两个线程t1和t2,我们可以通过调用t1.join()来确保t1执行完毕后再执行t2。 需要注意的是,join方法可能会抛出InterruptedException异常,这是因为线程在等待的过程可能会被断。当出现这种情况时,我们可以根据实际情况进行处理,例如可以选择重新进行等待或者直接抛出异常。 总之,join方法Java用于管理线程执行顺序的重要方法。它可以让线程按照我们预期的方式执行,提高线程的可控性和可靠性。 ### 回答3: 在Javajoin方法是Thread类的一个方法,用于等待一个线程的终止。 当调用一个线程对象的join方法时,当前线程将被阻塞,直到被调用join方法线程执行完毕才能继续执行。 join方法有两种重载形式: 1. join():等待被调用线程执行完毕。 2. join(long millis):等待被调用线程执行一定的时间。 join方法常用于需要等待子线程执行完毕后再执行的场景,特别是在主线程等待子线程的结果返回的情况。通过join方法,我们可以确保主线程在子线程完成后再继续执行。 当一个线程被调用join方法后,其他所有线程都会被阻塞,直到被调用join方法线程执行完毕。这种阻塞是无法被断的,也就是说其他线程无法通过断被调用join方法线程来解除阻塞状态。 如果被调用join方法线程在执行过程抛出了异常,那么调用join方法线程不会再等待,直接返回,继续执行自己的任务。 需要注意的是,如果调用join方法线程已经处于终止状态,那么调用join方法的效果就相当于没有调用join方法一样,即调用线程不会被阻塞。 总之,join方法是用于等待一个线程的终止的方法,主要用于协调多个线程的执行顺序和获取子线程的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值