java多线程基本概述(七)——join()方法

在很多情况下,主线程创建并启动子线程,如果子线程中有大量的耗时运算,主线程将早于子线程结束,如果想让主线程等待子线程结束后再结束,那么我们可以使用join()方法。调用join()方法的意思是当前线程使调用了该方法的线程执行完成然后再执行自己本身。api文档如下:

public final void join(long millis,
        int nanos)
                throws InterruptedException
Waits at most millis milliseconds plus nanos nanoseconds for this thread to die.
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Parameters:
millis - the time to wait in milliseconds
nanos - 0-999999 additional nanoseconds to wait
Throws:
IllegalArgumentException - if the value of millis is negative, or the value of nanos is not in the range 0-999999
InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown

简单翻译如下:调用该方法的线程会等待millils+nanos的时间,直到该线程执行完(调用该方法的Thread.isAlive()方法返回false时)。该方法的实现是使用循环判断该线程isAlive()方法,如果为true,那么就调用wait()方法进行等待。当线程结束时,notifyAll()方法被调用。如果调用join()后再调用notify()/notifyAll()则可能会时join()方法失效。源码如下:

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) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

例子:

package soarhu;


class Service{

    void readMethod(){
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(i);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

}


public class Test {
    public static void main(String[] args) throws Exception {
            Service o = new Service();
            new Thread(){
                @Override
                public void run() {
                    o.readMethod();
                }
            }.start();
        System.out.println("main.......");
    }
}

输出结果:

main.......
0
1
2
3
4

Process finished with exit code 0

结果分析:可知主线程方法先被执行完毕。如果要使子线程先执行完,然后执行主线程。那么可以调用join()方法。

package soarhu;


class Service{

    void readMethod(){
        try {
            for (int i = 0; i < 25; i++) {
                Thread.yield();
                System.out.println(i+" "+Thread.currentThread().getName());
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

}


public class Test {
    public static void main(String[] args) throws Exception {
            Service o = new Service();
           Thread t =  new Thread(){
                @Override
                public void run() {
                    o.readMethod();
                }
            };
           t.start();
           t.join();
        for (int i = 0; i < 10; i++) {
            System.out.println("main......."+i);
        }

    }
}

输出结果:

0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
5 Thread-0
6 Thread-0
7 Thread-0
8 Thread-0
9 Thread-0
10 Thread-0
11 Thread-0
12 Thread-0
13 Thread-0
14 Thread-0
15 Thread-0
16 Thread-0
17 Thread-0
18 Thread-0
19 Thread-0
20 Thread-0
21 Thread-0
22 Thread-0
23 Thread-0
24 Thread-0
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9

Process finished with exit code 0

可知确实是等待子线程执行完后才执行主线程。我们不用join()也可以参考api源码来实现join().如下:

public class Test {
    public static void main(String[] args) throws Exception {
        Service o = new Service();
        Thread t = new Thread() {
            @Override
            public void run() {
                o.readMethod();
            }
        };
        t.start();
        //t.join();
        synchronized (t) {
            do {
                t.wait();
            } while (t.isAlive());
        }
        for (int i = 0; i < 10; i++) {
            Thread.yield();
            System.out.println("main......." + i);
        }

    }
}

输出结果:

0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
5 Thread-0
6 Thread-0
7 Thread-0
8 Thread-0
9 Thread-0
10 Thread-0
11 Thread-0
12 Thread-0
13 Thread-0
14 Thread-0
15 Thread-0
16 Thread-0
17 Thread-0
18 Thread-0
19 Thread-0
20 Thread-0
21 Thread-0
22 Thread-0
23 Thread-0
24 Thread-0
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9

Process finished with exit code 0

可见join()方法的实现方式很简单,就是加一个同步方法,再内部循环判断该线程是否结束,如果未结束,则再jon()这个地方一致阻塞,导致下面的代码不能被执行,如果在调用join()方法后,再调用该线程的wait()方法则会发生死锁,调用notify()则可能发生死锁或者join()失效。例子如下:

package soarhu;


import java.util.concurrent.TimeUnit;

class Service {

    void readMethod() {
        try {
            for (int i = 0; i < 25; i++) {
                TimeUnit.MILLISECONDS.sleep(300);
                Thread.yield();
                while (i==5){  //1 line
                   synchronized (this){
                       //wait();           //    发生死锁
                       this.notifyAll();
                   }
                }
                System.out.println(i + " " + Thread.currentThread().getName());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


public class Test {
    public static void main(String[] args)  {
        Service o = new Service();
        Thread t = new Thread() {
            @Override
            public void run() {
                o.readMethod();
            }
        };
        t.start();
        try {
            t.join();
            for (int i = 0; i < 10; i++) {
                System.out.println("main......." + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

输出结果:死锁

0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0

由于join()内部是用的wait()方法,那么也可能会抛出中断异常,举例如下:

package soarhu;


import java.util.concurrent.TimeUnit;

class Service extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 25; i++) {
                TimeUnit.MILLISECONDS.sleep(300);
                Thread.yield();
                if (i==5){
                   this.interrupt();
                }
                System.out.println(i + " " + Thread.currentThread().getName());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


public class Test {
    public static void main(String[] args)  {
        Service o = new Service();
        o.start();
        try {
            o.join();
            System.out.println("isAlive? "+o.isAlive());
            for (int i = 0; i < 10; i++) {
                System.out.println("main......." + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

输出结果:

0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
5 Thread-0
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
isAlive? false
    at soarhu.Service.run(Test.java:12)
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9

Process finished with exit code 0

 示例:

package tij;

/**
 * Created by huaox on 2017/4/19.
 */

class Slepper extends Thread{
    private int duration;
    public Slepper(String name,int duration){
        super(name);
        this.duration = duration;
        start();
    }

    @Override
    public void run() {
        try {
            sleep(duration);
        }catch (InterruptedException e){
            System.out.println(getName()+" was interrupted ");
            return ;
        }
        System.out.println(getName()+" has awakened");
    }
}


class Joiner extends Thread{
    private Slepper slepper;
    public Joiner(String name,Slepper slepper){
        super(name);
        this.slepper = slepper;
        start();
    }

    @Override
    public void run() {
        try {
            slepper.join();
        }catch (InterruptedException e){
            System.out.println(getName()+" was interrupted ");
            return ;
        }
        System.out.println(getName()+" join completed");
    }
}


public class JoinTest {
    public static void main(String[] args) {
        Slepper slepper = new Slepper("slepper",1500);
        Slepper grumpy = new Slepper("grumpy",1500);
        Joiner dopey = new Joiner("dopyey->slepper",slepper);
        Joiner doc = new Joiner("doc->grumpy",grumpy);
        //grumpy.interrupt();  //line 1
    }

}

输出结果;

slepper has awakened
grumpy has awakened
dopyey->slepper join completed
doc->grumpy join completed

Process finished with exit code 0

可以看到都是等到join()方法结束后才开使执行自身线程,如果把line 1的注释取消掉

输出结果:

grumpy was interrupted     // 1
doc->grumpy join completed //2   这时,1和2一同结束  如果join()的所在线程发生异常,那么CurrentThread也将一同结束
slepper has awakened dopyey
->slepper join completed Process finished with exit code 0

此时

posted @ 2017-04-18 21:11  soar_hu  阅读(295)  评论(0编辑  收藏  举报