[Java] 线程之join

本文详细探讨了Java线程中的join方法,它用于控制线程执行顺序,确保一个线程在另一个线程执行完毕后再继续执行。通过示例代码展示了普通调用和在持有监视器锁时调用join的区别,强调了在持有锁的情况下调用join需谨慎,因为它会保持锁的占用直至join完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程里面有一个join方法,意思是等待该线程的消亡,即等待该线程被执行完。它还有带参数的重载方法,参数是等待的最长时间。一个线程自己对自己调用这个方法显然没什么意思,就表示自己再等一定的时间消亡,这没什么实际意义。一般是对另外一个线程调用这个方法,表示等待一段时间或直到另外一个线程执行完毕这个线程才继续往下执行。这说明了什么,很显然是在调整线程间的执行顺序,这也是join方法的一个重要的使用场景。join方法的调用也分为两种情况,一种是普通调用,一种是获得监视器锁的时候调用。同样,在获得监视器锁的时候调用,这个方法会一直占有锁,直到方法执行完毕后,锁才会被释放掉,所以这种情况下要慎用。

下面是普通调用的示例代码:

final Thread t1 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程1 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					Thread.sleep(5000);
					System.out.println("线程1 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		Thread t2 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程2 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					t1.start();
					t1.join();
					System.out.println("线程2 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		final Thread t3 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程3 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					Thread.sleep(5000);
					System.out.println("线程3 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		Thread t4 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程4 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					t3.start();
					t3.join();
					System.out.println("线程4 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		t2.start();
		Thread.sleep(1000);
		t4.start();

下面是执行结果:

线程2 -> 2012-10-31 09:33:16
线程1 -> 2012-10-31 09:33:16
线程4 -> 2012-10-31 09:33:17
线程3 -> 2012-10-31 09:33:17
线程1 <- 2012-10-31 09:33:21
线程2 <- 2012-10-31 09:33:21
线程3 <- 2012-10-31 09:33:22
线程4 <- 2012-10-31 09:33:22

线程2和线程4间隔一秒调用,在线程2里面启动线程1并调用它的join方法,在线程4里面启动线程3并调用它的join方法。从结果可以看出确实是在线程1执行完毕后线程2才继续执行,在线程3执行完毕后线程4才继续执行。而且它们的开始和结束时间都间隔一秒,说明对不同线程的join方法调用互不影响。这是普通调用的情况。

下面是获得监视器锁情况的示例代码:

final Object monitor = new Object();
		
		final Thread t1 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程1 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					Thread.sleep(5000);
					System.out.println("线程1 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		Thread t2 = new Thread(new Runnable(){
			
			public void run() {
				try {
						System.out.println("线程2 等待锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					synchronized(monitor) {
						System.out.println("线程2 获得锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
						t1.start();
						t1.join();
						System.out.println("线程2 退出锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					}
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		final Thread t3 = new Thread(new Runnable(){
			
			public void run() {
				try {
					System.out.println("线程3 -> " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					Thread.sleep(5000);
					System.out.println("线程3 <- " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		Thread t4 = new Thread(new Runnable(){
			
			public void run() {
				try {
						System.out.println("线程4 等待锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					synchronized(monitor) {
						System.out.println("线程4 获得锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
						t3.start();
						t3.join();
						System.out.println("线程4 退出锁 " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
					}
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}});
		
		t2.start();
		Thread.sleep(1000);
		t4.start();


下面是执行结果:

线程2 等待锁 2012-10-31 09:44:09
线程2 获得锁 2012-10-31 09:44:09
线程1 -> 2012-10-31 09:44:09
线程4 等待锁 2012-10-31 09:44:10
线程1 <- 2012-10-31 09:44:14
线程2 退出锁 2012-10-31 09:44:14
线程4 获得锁 2012-10-31 09:44:14
线程3 -> 2012-10-31 09:44:14
线程3 <- 2012-10-31 09:44:19
线程4 退出锁 2012-10-31 09:44:19

线程2和线程4间隔一秒调用,这样线程2就可以立马获得锁,然后启动线程1并调用它的join方法。这时线程4需要获得锁,但因为线程2持有了锁并一直没有释放,所以线程4在等待锁,直到线程1的join结束,线程2才继续执行,然后释放锁。这时线程4才获得锁,然后启动线程3并调用它的join方法,锁就被一直占有着,直到线程3的join方法结束,线程4继续执行,才释放掉锁。可以看出join方法会一直占有锁,直到阻止结束后才会释放。所以要慎用,这是获得监视器锁调用的情况。

如本文有错误之处,敬请之处,共同讨论!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值