Java多线程之wait(),notify(),notifyAll()

在多线程的状况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。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

**/
相关文章
相关标签/搜索