线程间通信-方法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。
- 线程间通信-方法join的使用
- JAVA多线程-线程间通信(四)-join的使用
- 线程中join()方法的使用
- python 线程中join方法的使用
- Java线程5:join方法的使用
- App11_03_线程中join()方法的使用
- Java多线程编程----线程间通信--join()方法详解
- Java基础——线程间的通信+生产者消费者程序+守护线程+Join方法+线程优先级
- 线程的join方法
- 线程的join方法
- 线程的join方法
- 线程的join方法
- 线程的join()方法
- 黑马程序员_day12 多线程(线程间通信,停止线程,守护线程,Join方法,优先级)
- 线程join()方法join的含义
- Java线程的join方法
- Java线程的join方法
- 线程的join方法练习
- 不持有焦点的跑马灯效果TextView
- # android 系统修改/dev/bus/usb/*和串口的访问权限
- Metro
- leetcode_c++:图:Clone Graph(133)
- [leetcode]153. Find Minimum in Rotated Sorted Array
- 线程间通信-方法join的使用
- 图片传输(APP端将图片传至服务器端存储)
- mysql安装与配置(转)
- HDU-5038 Grade (2014亚洲区北京站网络赛)
- Linux下访问MySQL的数据库权限不够的问题
- RTC闹钟的中断处理方法以及程序设计
- Canny边缘检测算子原理
- 思维的陷阱
- Eclipse