关于 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.
就这样一步一步的按照顺序执行。
五.结尾
就讲到这里吧,希望对大家有所帮助!相关文章推荐
- java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
- java Pattern和Matcher详解
- eclipse里面设置JVM参数的问题
- java 文件处理总结
- 隐马尔可夫模型的前向算法(java实现),今天奉上
- 利用eclipse export功能制作可执行的jar包
- Intellij 配置 JDK1.8 语法支持
- 菜鸟猿大战Java之IO系列(一)
- RxJava使用总结
- javaWeb Jsp入门和常用对象的使用
- java学习:数据库的使用
- Android studio 如何导入Eclipse项目
- Java web开发编码设置
- java throw throws 区别(转载)
- Struts2的配置
- freemarker中参数及java片段的使用方法
- Java读写Excel文件示例
- java socket传送和接受byte[]
- Java类集学习(六)应用范例(一对多的关系)
- eclipse jad 反编译 插件安装