线程(五)---join

写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!  

对于join()的概念如名字一样,当调用线程的join方法时,之后所有的操作需要等该线程结束才能继续(可以理解为插队),因此,可以很方便的使用join实现多个线程的串行执行,以下对使用join串行化执行多个线程的方式进行简单介绍,首先我创建一个线程任务类JoinTask,线程中执行的方法就是打印五次线程名字,代码如下:

public class JoinTask extends Thread {

    private String name;

    public JoinTask(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + name);
        }
    }
}

新建main方法进行测试:

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

        JoinTask t1 = new JoinTask("A");
        JoinTask t2 = new JoinTask("B");
        t1.start();
        t1.join(); 
        t2.start();
}

按照我对join的理解应该是等线程A执行结束之后再执行线程B,执行结果也确实如此,执行结果如下:

join()方法可以带一个时间参数,如:join(10),它表示当线程调用此join方法后,其它线程只会等待10毫秒,10毫秒之后会并行执行。演示代码还是基于上一个例子,为了演示效果,在任务类JoinTask中增加sleep(1000),任务1的join的等待时间设置为2000毫秒,t1.join(2000),代码如下:

public class JoinTask extends Thread {

    private String name;

    public JoinTask(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " : " + name);
        }
    }
}
public static void main(String[] args) throws Exception {
        JoinTask t1 = new JoinTask("A");
        JoinTask t2 = new JoinTask("B");
        t1.start();
        t1.join(2000); 
        t2.start();
}

按照对join(long millis)方法的理解,应该是t1线程启动后,过两秒t2线程也启动,这时候两个线程同时执行,执行结果确实也是如此,执行结果如下:

测试发现将join方法放到t1,t2线程start()之前和之后都不起作用,这是为什么呢?

join的源码如下:

可以看出,join是基于wait()实现的,当join()方法放到前面时,因为线程并未启动,执行到1251行时isAlive()返回的是false,所以join不会生效,但是当放到两个线程中间的时候,线程t1已经启动,那么isAlive()会返回true,会进入到wait(0)一直等待,直到t1线程执行完才能执行之后的代码,放到后面不生效的原理和这个差不多。PS:join源码中,只会调用wait方法,并没有在结束时调用notify,这是因为线程在执行完的时候会自动调用自身的notifyAll方法,来释放所有的资源和锁。

换一种说法:

main函数是jvm运行的起点,执行main函数jvm就会运行起来,并创建main线程,当主线程调用t1.join()方法时,主线程先获得了t1对象的锁,随后进入方法,调用了t1对象的wait()方法,使主线程进入了t1对象的等待池,此时,A线程则还在执行,并且随后的t2.start()还没被执行,因此,B线程也还没开始。等到A线程执行完毕之后,主线程继续执行,走到了t2.start(),B线程才会开始执行。

下面结合main线程测试一下join所在位置对线程执行顺序的影响:

以最初的JoinTask为例:

public class JoinTask extends Thread {

    private String name;

    public JoinTask(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + name);
        }
    }
}
public static void main(String[] args) throws Exception {
       
        System.out.println(Thread.currentThread().getName()+" start");
        JoinTask t1=new JoinTask("A");
        JoinTask t2=new JoinTask("B");
        JoinTask t3=new JoinTask("C");
        System.out.println("t1start");
        t1.start();
        System.out.println("t2start");
        t2.start();
        System.out.println("t3start");
        t3.start();
        System.out.println(Thread.currentThread().getName()+" end");
}

执行结果如下:

从上面可以看出A、B、C和主线程交替运行,从打印结果来看,主线程在启动后调用了A、B、C三个线程的start方法后就结束了,主线程结束了A、B、C三个线程才开始打印,这也侧面说明了主线程是可以提前结束的,还有我们知道调用线程的start(),是线程进入可运行状态,但并不是真的运行,一旦获取到cpu时间片,就开始run(),这时才真正的运行起来,才开始打印。

网上有人说:t.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。接下来验证下是否正确,为了更好的验证效果,将JoinTask打印方法延迟10毫秒执行,新建JoinTask1并设置延迟20毫秒,保证在线程A执行完之后,线程B还没执行完。

基于以上方法加入join后代码如下:

public static void main(String[] args) throws InterruptedException {
		
		System.out.println(Thread.currentThread().getName()+" start");
		ThreadTest t1=new ThreadTest("A");
		ThreadTest t2=new ThreadTest("B");
		ThreadTest t3=new ThreadTest("C");
		System.out.println("t1start");
		t1.start();
		System.out.println("t1end");
		System.out.println("t2start");
		t2.start();
		System.out.println("t2end");
		t1.join();
		System.out.println("t3start");
		t3.start();
		System.out.println("t3end");
		System.out.println(Thread.currentThread().getName()+" end")
}

改造后的JoinTask代码如下:

public class JoinTask extends Thread {

    private String name;

    public JoinTask(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " : " + name);
        }
    }
}

新增的JoinTask1代码如下:

public class JoinTask extends Thread {

    private String name;

    public JoinTask(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " : " + name);
        }
    }
}

执行结果如下:

无论执行多少次我发现线程C总会在线程A执行完之后再执行,当调用线程A的join()方法阻塞线程A的时候,线程B一直在运行,这也验证了,t.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒,并不影响同一时刻处在运行状态的其他线程的结论。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值