在多线程的状况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。java
wait与notify是java同步机制中重要的组成部分。结合与synchronized关键字使用,能够创建不少优秀的同步模型。
synchronized(this){ }等价于publicsynchronized void method(){.....}
同步分为类级别和对象级别,分别对应着类锁和对象锁。类锁是每一个类只有一个,若是static的方法被synchronized关键字修饰,则在这个方法被执行前必须得到类锁;对象锁类同。
首先,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在做用等同于synchronized(obj){......}的内部才可以去调用obj的wait与notify/notifyAll三个方法,不然就会报错:
java.lang.IllegalMonitorStateException:current thread not owner
在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次得到了去得到对象锁的权利。
因此,notify与notifyAll没有太多的区别,只是notify仅唤醒一个线程并容许它去得到锁,notifyAll是唤醒全部等待这个对象的线程并容许它们去得到对象锁,只要是在synchronied块中的代码,没有对象锁是步履维艰的。其实唤醒一个线程就是从新容许这个线程去得到对象锁并向下运行。多线程
notifyAll,虽然是对每一个wait的对象都调用一次notify,可是这个仍是有顺序的,每一个对象都保存这一个等待对象链,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,须要注意一下。this
wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每一个对像都有wait(),notify(),notifyAll()的功能。由于都个对像都有锁,锁是每一个对像的基础,固然操做锁的方法也是最基础了。spa
wait():线程
等待对象的同步锁,须要得到该对象的同步锁才能够调用这个方法,不然编译能够经过,但运行时会收到一个异常:IllegalMonitorStateException。code
调用任意对象的 wait() 方法致使该线程阻塞,该线程不可继续执行,而且该对象上的锁被释放。orm
notify():对象
唤醒在等待该对象同步锁的线程(只唤醒一个,若是有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM肯定唤醒哪一个线程,并且不是按优先级。进程
调用任意对象的notify()方法则致使因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到得到锁后才真正可执行)。get
notifyAll():
唤醒全部等待的线程,注意唤醒的是notify以前wait的线程,对于notify以后的wait线程是没有效果的。
一般,多线程之间须要协调工做:若是条件不知足,则等待;当条件知足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。
例如:
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
当线程A得到了obj锁后,发现条件condition不知足,没法继续下一处理,因而线程A就wait()。
在另外一线程B中,若是B更改了某些条件,使得线程A的condition条件知足了,就能够唤醒线程A :
synchronized(obj) {
condition = true;
obj.notify();
}
须要注意的概念是:
# 调用obj的wait(), notify()方法前,必须得到obj锁,也就是必须写在synchronized(obj){...} 代码段内。
# 调用obj.wait()后,线程A就释放了obj的锁,不然线程B没法得到obj锁,也就没法在synchronized(obj){...} 代码段内唤醒A。
# 当obj.wait()方法返回后,线程A须要再次得到obj锁,才能继续执行。
#若是A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪个由JVM决定)。
#obj.notifyAll()则能所有唤醒A1,A2,A3,可是要继续执行obj.wait()的下一条语句,必须得到obj锁,所以,A1,A2,A3只有一个有机会得到锁继续执行,例如A1,其他的须要等待A1释放obj锁以后才能继续执行。
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,所以,A1,A2,A3虽被唤醒,可是仍没法得到obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会得到锁继续执行。
谈一下synchronized和wait()、notify()等的关系:
1.有synchronized的地方不必定有wait,notify
2.有wait,notify的地方必有synchronized.这是由于wait和notify不是属于线程类,而是每个对象都具备的方法,并且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。
另外,注意一点:若是要把notify和wait方法放在一块儿用的话,必须先调用notify后调用wait,由于若是调用完wait,该线程就已经不是currentthread了。
public class E10_9 { public static void main(String[] args) { ThreadB b = new ThreadB(); ThreadC c = new ThreadC(); c.setName("第二线程"); b.setName("第一线程"); c.start(); System.out.println(Thread.currentThread().getName() + " is running.."); synchronized(c){ try{ System.out.println("Waiting for b1 to complete..."); c.wait(); System.out.println("Completed. Now back to " + Thread.currentThread().getName()); b.start(); }catch(InterruptedException e){ e.printStackTrace(); } } } } class ThreadB extends Thread{ int total; public void run(){ synchronized(this){ System.out.println(Thread.currentThread().getName() + " is running.."); for(int i = 0; i < 10; i++){ total += i; } System.out.println("total is " + total); } } } class ThreadC extends Thread{ int sum = 1; public void run(){ synchronized(this){ System.out.println(Thread.currentThread().getName() + " is running.."); for(int i = 1; i < 10; i++){ sum *= i; } System.out.println("sum is " + sum); this.notify(); } } } /** main is running.. 第二线程 is running.. sum is 362880 Waiting for b1 to complete... Completed. Now back to main 第一线程 is running.. total is 45 **/