java 线程方法join的自我总结

首先详细介绍一下join( )方法的源码

public final synchronized void join(long millis)	//参数表示毫秒数
    throws InterruptedException {
    
    	/*System.currentTimeMillis()方法是调用系统当前的毫秒数,
    	 *日期1970年1月1日00:00:00 GMT之后的毫秒数*/
        long base = System.currentTimeMillis();
        long now = 0;
		
		//如果传入的参数小于0,跑出异常
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

		//如果参数等于0时执行下面循环
        if (millis == 0) {
        	
        	/*isAlive():如果当前线程是存活的,返回true,否则返回false
        	 *判断线程是否存活,是的话执行wait(0);*/
            while (isAlive()) {
            
          		//让当前活着的线程等待,这里的0不是等待0毫秒,0表示无限等待,直至线程被唤醒
          		//注意:是指当前运行的线程会等待,不是指调用了wait的线程会等待
                wait(0);	
            }
        } else {		
        	//如果参数大于0
            while (isAlive()) {	//线程是否存活
                long delay = millis - now;		//计算需要等待多少秒
                if (delay <= 0) {
                    break;	//到时了,跳出循环
                }
                wait(delay);	//等待delay毫秒
                now = System.currentTimeMillis() - base;	//现在过了几毫秒
            }
        }
    }

简单的来说,join(参数)方法,其实就是让当前的线程等待(调用wait()会释放锁),然后就绪状态下的线程可以获得锁运行,运行结束后,之前等待的所有线程全部被唤醒,相当于调用了notifyAll()方法。下面看个图先:
在这里插入图片描述

看完图后接着讲关于join方法的例子

public class JoinThreadTest {

	public static void main(String[] args) throws InterruptedException {

		//线程1
		Thread t = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					System.out.println("a:" + i);
				}
			}
		};

		//线程2
		Thread t2 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					System.out.println("b:" + i);
				}
			}
		};

		//线程3
		Thread t3 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println("c:" + i);
				}
			}
		};

		//由上面图片可以知道,调用start()方法并不是立马执行,而是让线程进入就绪状态,等待锁的竞争
		t.start();		
		t.join();		
		t3.start();
		t2.start();
		for (int i = 10; i < 20; i++)
			System.out.println(123456);
	}
}
在这里我主要讲解一些东东:

上面代码其实有四个线程,除了t,t2,t3还有main,main就是主线程。下面从 t.start();讲到尾
注意:在讲到 t.start();时,目前只有主线程在运行

  1. 首先执行了t.start(),则线程t进入就绪状态,等着和主线程竞争锁。
  2. 接着执行t.join(), 这里的t虽然调用了join( )方法,但是不是指让t去等待,上面源码说了,是让当前活着的线程等待,而目前活着的只有主线程(如果主线程不是活的他在怎么会执行t.join()),所以主线程会进入等待,直到t运行结束后,等待中的所有线程被唤醒,所以t执行完毕之后,会默认调用notifyAll(),唤醒主线程,让主线程从之前位置继续运行。
  3. 接着又执行t3.start(),则t3进入等待,和主线程竞争锁。
  4. 最后执行t2.start(),则t2也进入等待,和主线程和t3一起竞争抢锁。
  5. 上面代码讲至此,接着我说一下运行的结果(以上数据只是参考,数据太少可能看不效果,读者可以尝试让数据多一点就有效果了):

运行结果:首先t全部执行完毕,输出0-9,然后t3和t2和主线程交替输出(并发执行输出)



讲到这里你可能还不完全明白,我接着讲

如果上面部分代码变成下面的,那么是什么个情况?

		t.start();		
		t3.start();
		t3.join();		// join()换这里来了
		
		for (int i = 10; i < 20; i++)
			System.out.println(123456);
		t2.start();
  1. 首先执行t.start();则t进入就绪状态,和主线程竞争锁,所以此过程中可能会出现这种情况
    (one:t线程运行特别特别短然后结束了。two:t线程有点长,t运行了一下下,主函数接着运行,然后接着t运行…。three:t运气不好,还没得到锁)

  2. 主函数接着运行t3.start();则t3进入就绪状态(在此过程中t可能在就绪状态【three】,也可能在运行【two】,也可能运行完了【one】)。

  3. 接着执行t3.join(),则此时对应上面的one、two、three分为几种可能情况:
    【one】情况:t执行结束了即t线程死亡了,活着只有主线程,主线程执行t3.join() ,则主线程等待(因为当前轮到主线程运行),直至t3执行完毕,主线程唤醒
    【two】情况:t可能运行到一半,接着执行了join(),此时存活的线程有t和main,但是执行t3.join()的是主线程,说明目前cpu是主线程在用,所以刚好主线程被wait()了,所以主线程等待,t和t3会交替执行。直至t3结束主线程才会醒(main唤醒和t3(调用join方法者)是否结束有关,和t无关),若此时t运行没结束,则和main一起交替执行。
    【three】情况:执行join()时,t可能还在就绪状态,而main调用join(),则mian进入等待,而就绪的t和t3竞争锁,并发交替执行,直至t3结束,mian才会被唤醒。

  4. 最后执行for循环和t2.start(),for是属于主线的内容,会比t2优先执行



最后一个例子
		t.join();		// join()换这里来了
		t.start();		
		t3.start();
		
		for (int i = 10; i < 20; i++)
			System.out.println(123456);
		t2.start();
  1. 这里的t.join有和没有效果都一样,为什么呢?我举个不太好的例子
  2. 比如,有4个跑步比赛的参与者,start()就表示他们已经进场了,准备跑步
  3. 而run()就表示他们已经在跑了
  4. 然后到场的每个人都有一个神奇的功能,就是join(),他可以让那个刚好在跑步的人停下来,等他跑完了,再让停下来的那个继续跑。
  5. 而上面代码先调用join再调用start,就相当于这个人还没到场就先有功能了,就和第4冲突了。
  6. 所以就是说,你这个线程都还没有start(),执行join()怎么可能会有效。
  7. 结论就是:只有先执行了start,join才会生效,反过来的话join不执行。



最最后一个例子
		t.start();		
		t.join();	
		t3.start();
		t3.join();		
		for (int i = 10; i < 20; i++)
			System.out.println(123456);
		t2.start();
		t2.join();
到这里大家可能知道运行结果了,但我还是要讲一下子
  1. 首先主线程执行t.start(); t进入就绪状态
  2. 主线执行t.join(); 则主线程等待,后面代码主线程无法运行
  3. t线程结束,主线程醒了继续执行t3.start(); t3进入就绪
  4. 主线程执行t3.join(); 则主线程等待,后面代码主线程无法运行
  5. t3线程结束,主线程醒了继续执行for循环语句。
  6. 接着主线程执行t2.start(); t2进入就绪状态
  7. 主线程执行t2.join(); 则主线程等待,后面代码主线程无法运行
  8. 最终结果就是:输出t所以值,然后输入t3所以值,然后输出for所有值,然后输出t2所有值



总结一下子

通过上面例子可以知道,执行了join方法,其实就是让主线程等待,只要主线程一直活着while(isAlive()),就不让他醒来wait(0),除非把那个让主线程醒不来的线程想方法给干死(stop()【不建议】或者其他方式),不然主线程不会醒。





== 个人总结,有哪里错误的地方请指正,THX ==
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值