您的位置:首页 > 编程语言 > Java开发

关于 JAVA wait()和notify()

2015-12-30 22:37 330 查看

一.概述

wait,notify和notifyAll方法是Object类的成员函数,所以Java的任何一个对象都能够调用这三个方法。这三个方法主要是用于线程间通信,协调多个线程的运行。

大家要知道,在JAVA中,是没有类似于PV操作、进程互斥等相关的方法的。JAVA的进程同步都是通过synchronized来实现的,一般来说多个线程互斥访问某个资源,用synchronized就够了,但如果需要在线程间相互唤醒的话就需要借助Object.wait(), Object.nofity()了。

二.wait函数

打个比方:wait函数表示:我累了,想休息一会儿(同时本线程休眠),对象的锁你们拿去用吧,CPU也给你们。

需要注意一下几点:

1.调用了wait函数的线程会一直等待(本线程休眠),直到有其他线程调用了同一个对象的notify或者notifyAll方法才能被唤醒。如果没有其他线程调用该对象的notify或者notifyAll方法,则该线程将会永远等下去…

2.被唤醒并不代表立即获得对象的锁。

也就是说

3.wait函数必须在同步代码块中调用(也就是当前线程必须持有对象的锁)

注意 wait和sleep不同,sleep是不会释放对象锁的,但是wait是会释放对象锁的。

三.notify和notifyAll方法

还是先打个比方:女士们,先生们请注意了,这个锁的对象我即将用完,请大家醒醒(这是在唤醒该加锁对象的线程),准备一下,马上你们就能使用锁了。

需要注意一下几点:

1.otofy/notifyAll方法也必须在同步代码块中调用(也就是调用线程必须持有对象的锁)

2.notify方法只会唤醒一个正在等待的线程(至于唤醒谁,不确定!),而notifyAll方法会唤醒所有正在等待的线程

3.调用notify和notifyAll方法后,当前线程并不会立即放弃锁的持有权,而必须要等待当前同步代码块执行完才会让出锁。

4.如果一个对象之前没有调用wait方法,那么调用notify方法是没有任何影响的。

四.举个例子能更好的理解

这是一道比较经典的面试题:三线程打印ABC的问题,对Object.wait(),Object.notify()应用的最好理解。题目要求如下:

建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。代码如下:

public class MyThreadPrinter implements Runnable {

private String name;

private Object next;

private Object self;

public MyThreadPrinter(String name, Object next, Object self) {
this.name = name;
this.next = next;
this.self = self;
}

@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (self) {
synchronized (next) {
System.out.print(name);
count--;
next.notify();
}
try {
self.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}


}

public class Main {

public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter pa = new MyThreadPrinter("A", b, a);
MyThreadPrinter pb = new MyThreadPrinter("B", c, b);
MyThreadPrinter pc = new MyThreadPrinter("C", a, c);

new Thread(pa).start();
try {
//这个睡眠是非常有必要的。如果不这样,abc这3个线程不知道谁先执行,打印就会不准确,这样可以保证TheadA-->B---C,顺序执行。
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}

new Thread(pb).start();
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}

new Thread(pc).start();
}


}

打印输出:ABCABCABCABCABCABCABCABCABCABC

解释一下这段代码:

我们的首要目的是要求:按照ThreadA->ThreadB->ThreadC->ThreadA循环执行三个线程。

那好:

ThreadA打印的条件:获取a,b

ThreadB打印的条件:获取b,c

ThreadC打印的条件:获取c,a

第一步:一开始ThreadA先获取a,然后获取b,打印完毕后,通知TheadB:“b资源我要用完了,你快醒醒吧!”。先释放b,再释放a.

第二步:因为b优先a被释放,所以ThreadB先执行(a被占用,ThreadC无法执行),先获取b,再获取c,打印b,先释放c,再释放b.

第三步:因为c优先b被释放,所以ThreadC先执行(b被占用,ThreadA无法执行),先获取c,再获取a,打印c,先释放a,再释放c.

就这样一步一步的按照顺序执行。

五.结尾

就讲到这里吧,希望对大家有所帮助!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: