zoukankan      html  css  js  c++  java
  • 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

    此时

  • 相关阅读:
    2014年互联网发展趋势如何
    服务器出现阶段性错误
    用互联网思想武装自己
    杭州互联网公司汇总
    互联网牛人网址大全
    ffmpeg开发指南
    Windows下FFmpeg快速入门
    FFmpeg介绍及参数详细说明
    windows 下FFMPEG的编译方法 附2012-9-19发布的FFMPEG编译好的SDK下载
    FFMPEG视音频编解码零基础学习方法 【荐】
  • 原文地址:https://www.cnblogs.com/soar-hu/p/6730461.html
Copyright © 2011-2022 走看看