线程间通信

来源:互联网 发布:软件测试原理与实践 编辑:程序博客网 时间:2024/06/07 05:39

一:线程间通信

线程间通常需要通信协作来完成某项任务。在java中可通过wait,notify,和notifyAll方法实现线程间通信。


二:wait,notify,和notifyAll方法说明

只能在同步方法或者同步块里才能调用这3个方法。因为只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。若不满足这一条件,程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

1.  wait()方法: 

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。该方法会释放对象上的锁,从而使得其他线程可以获得这个锁,因此该对象中的其他同步方法可以在wait期间被调用。

2.  notify()方法 

唤醒在此对象监视器(也叫锁)上等待的单个线程。调用notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。 

3.  notifyAll()方法

notifyAll()方法和notify起到类似作用,区别在于,调用 notifyAll()方法将把因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。


三:线程间通信示例

写一个程序计算1到100的和,子线程负责计算,主线程返回结果。


(1)首先来一个错误的做法:

public class CommunicationTest {public static void main(String[] args) {Sub sub = new Sub();sub.start();/*try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}*/synchronized (sub) {// 主线程拥有sub对象的锁try {System.out.println("等待子线程完成计算......");sub.wait();// 主线程等待} catch (InterruptedException e) {e.printStackTrace();}System.out.println("计算结果是:" + sub.getResult());}}}class Sub extends Thread {private int result = 0;;public int getResult() {return result;}@Overridepublic void run() {synchronized (this) {// 计算1到100的和for (int i = 1; i <= 100; i++) {result += i;}System.out.println("我已经算好了!");notify();// 计算完毕,唤醒主线程}}}

说明:

1.  在main方法中调用sub.wait(),是让主线程等待,而不是子线程sub。

2.  该程序容易发生死锁,因为如果是线程sub先执行,并且在主线程调用wait方法之前,线程sub就调用了notify方法,那么当主线程调用wait方法后,由于线程sub不会再调用notify方法,因此主线程就会一直等待下去,从而导致死锁。打开注释,让主线程休眠,先让线程sub运行就能看到死锁效果。


(2)修改版本

public class CommunicationTest {public static void main(String[] args) {final Calculator caculator = new Calculator();// 子线程new Thread(new Runnable() {@Overridepublic void run() {caculator.caculate();}}).start();// 主线程System.out.println("计算结果是:" + caculator.getResult());}}class Calculator {private int result = 0;private boolean shouldCaculate = true;// 先计算public synchronized void caculate() {while (!shouldCaculate) {// 用while循环是为了防止假唤醒try {wait();} catch (InterruptedException e) {e.printStackTrace();}}for (int i = 1; i <= 100; i++) {result += i;}shouldCaculate = false;notify();}public synchronized int getResult() {while (shouldCaculate) {// 如果还没有计算,则等待try {wait();} catch (InterruptedException e) {e.printStackTrace();}}shouldCaculate = true;notify();return result;}}

说明:

修改版本将计算和返回结果的方法封装起来,并在封装的类中实现方法互斥,而不是在线程代码。此外,还用了一个布尔值判断是否已经计算过了,这样即使getResult方法在调用wait方法之前,caculate方法就调用了notify方法,也不会引起死锁,因为getResult发现已经计算了,就不会等待了。




0 0