线程间通信-方法join的使用

来源:互联网 发布:网络角色扮演游戏排行 编辑:程序博客网 时间:2024/05/17 06:02

  在多数情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时计算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完了再结束。比如子线程处理一个数据,主线程要取到这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。

学习join方法前的铺垫:

创建如下代码:

public class MyThread extends Thread{    @Override    public void run() {        int sencondValue = (int)(Math.random()*1000);        System.out.println(sencondValue);        try {            Thread.sleep(sencondValue);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
public class Run {    public static void main(String[] args) {        MyThread myThread = new MyThread();        myThread.start();        //Thread.sleep(?);        System.out.println("当myThread对象执行完毕后再执行");        System.out.println("但上面代码的sleep的值写多少");        System.out.println("答案是不确定");    }}

程序运行结果如下:
这里写图片描述

用join()方法来解决:

方法join可以解决这个问题。创建如下代码:

public class MyThread extends Thread{    @Override    public void run() {        int sencondValue = (int)(Math.random()*1000);        System.out.println(sencondValue);        try {            Thread.sleep(sencondValue);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
public class Run {    public static void main(String[] args) throws InterruptedException {        MyThread myThread = new MyThread();        myThread.start();        myThread.join();        System.out.println("当对象myThread执行完毕后再执行");    }}

执行结果如下:
这里写图片描述
  方法join的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期阻塞,等待线程x销毁后再继续执行线程z后面的代码。
方法join具有使线程排队运行的作用,有些类似同步的效果。join与synchronized的区别是:join内部是使用wait()方法进行等待的,而synchronized关键字是使用的是“对象监视器”原理做为同步。

方法join与异常:

在join过程中,如果当前线程对象被中断,则当前线程出现异常。创建如下代码:

public class ThreadA extends Thread{    @Override    public void run() {        for(int i=0;i<Integer.MAX_VALUE;i++){            String newString = new String();            Math.random();        }    }}
public class ThreadB extends Thread {    @Override    public void run() {        try {            ThreadA ta = new ThreadA();            ta.start();            ta.join();            System.out.println("线程B在runend处打印了");        } catch (InterruptedException e) {            System.out.println("线程B在catch处打印了");            e.printStackTrace();        }    }}
public class ThreadC extends Thread{    private ThreadB threadb;    public ThreadC(ThreadB threadb){        this.threadb = threadb;    }    @Override    public void run() {        threadb.interrupt();    }}
public class Run {    public static void main(String[] args) {        try{            ThreadB b = new ThreadB();            b.start();            Thread.sleep(500);            ThreadC c = new ThreadC(b);            c.start();        }catch(Exception e){            e.printStackTrace();        }    }}

执行结果如下:
这里写图片描述
说明方法join()与interrupt()方法如果彼此相遇,则会出现异常。但进程按钮还是呈红色状态,原因是线程ThreadA还在继续运行,线程ThreadA并未出现异常,是正常的状态。

方法join(long)的使用:

方法join(long)中的参数是设定等待的时间。创建如下代码:

public class MyThread extends Thread{    @Override    public void run() {        System.out.println("begin Time="+System.currentTimeMillis());        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
public class Run {    public static void main(String[] args) throws InterruptedException {        MyThread myThread = new MyThread();        myThread.start();        myThread.join(2000);        System.out.println("  end Time="+System.currentTimeMillis());    }}

执行结果如下:
这里写图片描述
运行结果等待了2秒。
但将main方法中的代码改为使用sleep(2000)方法时,运行的效果还是等待了2秒。如下所示:

public class Run {    public static void main(String[] args) throws InterruptedException {        MyThread myThread = new MyThread();        myThread.start();        // myThread.join(2000);        Thread.sleep(2000);        System.out.println("  end Time=" + System.currentTimeMillis());    }}

这里写图片描述

  那使用sleep(2000)和使用join(2000)有什么区别,从上面的例子运行效果是看不出区别的,其实主要区别还是来自于这2个方法对同步的处理上。

方法join(long)和sleep(long)的区别:

方法join(long)的功能在内部是使用wait(long)方法来实现的,所有join(long)方法具有释放锁的特点,方法join(long)的源码如下:

 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;            }        }}

从源码中可以了解到,当执行wait(long)方法后,当前线程的锁被释放,那么其他线程就可以调用此线程中的同步方法了。
而Thread.sleep(long)方法却不释放锁。创建如下代码:

public class ThreadB extends Thread{    @Override    public void run() {        System.out.println(" b run begin timer="+System.currentTimeMillis());        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(" b run end   timer="+System.currentTimeMillis());    }    synchronized public void bService(){        System.out.println("打印了 bService timer="+System.currentTimeMillis());    }}
public class ThreadA extends Thread{    private ThreadB threadB;    public ThreadA(ThreadB threadB){        this.threadB = threadB;    }    @Override    public void run() {        synchronized(threadB){            threadB.start();            try {                Thread.sleep(6000);                //Thread.sleep()方法不释放锁            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
public class ThreadC extends Thread{    private ThreadB threadB;    public ThreadC(ThreadB threadB){        this.threadB = threadB;    }    @Override    public void run() {        threadB.bService();    }}
public class Run {    public static void main(String[] args) throws InterruptedException {        ThreadB threadB = new ThreadB();        ThreadA threadA = new ThreadA(threadB);        threadA.start();        Thread.sleep(2000);        ThreadC threadC = new ThreadC(threadB);        threadC.start();    }}

执行结果如下:
这里写图片描述
由于线程threadA使用了Thread.sleep(long)方法一直持有ThreadB的对象锁,时间达到6秒,所以线程ThreadC只有在ThreadA时间达到6秒后释放ThreadB的锁时,才可以调用ThreadB中的同步方法synchronized public void bService()。
此实验也说明Thread.sleep(long)方法不释放锁。
修改ThreadA类如下:

public class ThreadA extends Thread{    private ThreadB threadB;    public ThreadA(ThreadB threadB){        this.threadB = threadB;    }    @Override    public void run() {        synchronized(threadB){            threadB.start();            try {                //Thread.sleep(6000);                //Thread.sleep()方法不释放锁                threadB.join();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

执行结果如下:
这里写图片描述
由于线程ThreadA释放了ThreadB的锁,所以线程ThreadC可以调用线程ThreadB的同步方法synchronized public void bService()。
这个实验也说明join(long)方法具有释放锁的功能。

方法join()后面的代码提前运行:出现意外

如果不注意使用join(long)方法的使用,就会掉进“陷进”里。创建如下代码:

public class ThreadA extends Thread{    private ThreadB threadB;    public ThreadA(ThreadB threadB){        this.threadB = threadB;    }    @Override    public void run() {        synchronized(threadB){            System.out.println("begin A ThreadName="+Thread.currentThread().getName()+" "+System.currentTimeMillis());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("end   A ThreadName="+Thread.currentThread().getName()+" "+System.currentTimeMillis());        }    }}
public class ThreadB extends Thread{    @Override    synchronized public void run() {        System.out.println("begin B ThreadName="+Thread.currentThread().getName()+" "+System.currentTimeMillis());        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("end   B ThreadName="+Thread.currentThread().getName()+" "+System.currentTimeMillis());    }}
public class Run {    public static void main(String[] args) throws InterruptedException {        ThreadB threadB = new ThreadB();        ThreadA threadA = new ThreadA(threadB);        threadA.start();        threadB.start();        threadB.join(2000);        System.out.println("main end "+System.currentTimeMillis());    }}

执行结果如下:
这里写图片描述

这里写图片描述
为什么会出现不同的结果呢?
为了查看join方法在Run类中执行的时机,创建如下代码:

public class RunFirst {    public static void main(String[] args) {        ThreadB threadB = new ThreadB();        ThreadA threadA = new ThreadA(threadB);        threadA.start();        threadB.start();        System.out.println("                   main end "+System.currentTimeMillis());    }}

执行结果如下:
第一次运行结果:
这里写图片描述
第二次运行结果:
这里写图片描述
通过多次运行RunFirst类后,可以发现一个规律:main end往往都是第一个打印的。所以可以完全确定地得出一个结论:方法join(2000)大部分是先执行的,也就是先抢到ThreadB的锁,然后快速释放。
而执行Run类就会出现一些不同的结果。先看下面:
这里写图片描述
1、b.join(2000)方法先抢到B的锁,然后将B锁进行释放。
2、ThreadA抢到锁,打印ThreadA begin并且Thread.sleep(5000)。
3、ThreadA打印ThreadA end。
4、这时join(2000)和ThreadB争抢锁,而join(2000)再次获得锁,发现时间已过,释放锁后打印main end。
5、ThreadB获得锁,并且Thread.sleep(5000)。
6、5秒后打印ThreadB end。
再看下图
这里写图片描述
1、 join(2000)方法抢到B锁,然后马上释放。
2、 ThreadB抢到锁,打印ThreadB begin并且Thread.sleep(5000).
3、 5秒后打印ThreadB end,释放锁。
4、 这时join(2000)和ThreadA争抢锁,ThreadA获得锁,打印ThreadA begin并且Thread.sleep(5000)。
5、 5秒后打印ThreadA end。
6、 最后打印main end。

0 0
原创粉丝点击