JAVA线程间协作:wait.notify.notifyAll
明确这个原理。就能理解为什么synchronized(this)与synchronized(static XXX)的差别了。synchronized就是针对内存区块申请内存锁,thiskeyword代表类的一个对象,所以其内存锁是针对同样对象的相互排斥操作,而static成员属于类专有,其内存空间为该类全部成员共同拥有。这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的全部成员间实现相互排斥。在同一时间仅仅有一个线程可訪问该类的实例。
假设仅仅是简单的想要实如今JAVA中的线程相互排斥,明确这些基本就已经够了。
可是假设须要在线程间进行协作通信,就须要借助Object对象的wait,notify和notifyAll方法了。
对应的notify()就是对对象锁的唤醒操作。
但有一点须要注意的是notify()调用后,并非立即就释放对象锁的,而是在对应的同步块或同步方法中运行结束,自己主动释放锁后。JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁。唤醒线程,继续运行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都能够暂停当前线程,释放CPU控制权,基本的差别在于Object.wait()在释放CPU同一时候。释放了对象锁的控制。
调用线程的sleep,yield方法时,线程并不会让出对象锁,wait却不同。
wait函数必须在同步代码块中调用(也就是当前线程必须持有对象的锁),他的功能是这种:
我累了。歇息一会儿。对象的锁你们拿去用吧。CPU也给你们。
调用了wait函数的线程会一直等待,直到有其它线程调用了同一个对象的notify或者notifyAll方法才干被唤醒,须要注意的是:被唤醒并不代表立即获得对象的锁。也就是说,一个线程调用了对象的wait方法后,他须要等待两件事情的发生:
假设一个线程调用了某个对象的wait方法,可是兴许并没有其它线程调用该对象的notify或者notifyAll方法。则该线程将会永远等下去…
notify/notifyAll方法也必须在同步代码块中调用(也就是调用线程必须持有对象的锁),他们的功能是这种:
女士们。先生们请注意,锁的对象我即将用完,请大家醒醒,准备一下。立即你们就能使用锁了。
不同的是。notify方法仅仅会唤醒一个正在等待的线程(至于唤醒谁,不确定!)。而notifyAll方法会唤醒全部正在等待的线程。
另一点须要特别强调:调用notify和notifyAll方法后,当前线程并不会立即放弃锁的持有权,而必须要等待当前同步代码块运行完才会让出锁。
假设一个对象之前没有调用wait方法,那么调用notify方法是没有不论什么影响的。
以下来看一个样例:
package com.cooperation; import java.util.concurrent.TimeUnit; public class Test { public static Object object = new Object(); static class Thread1 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" get the lock."); } } } static class Thread2 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); object.notify(); System.out.println(Thread.currentThread().getName()+" invoke notify()"); System.out.println(Thread.currentThread().getName()+" release the lock."); } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Thread1()); Thread thread2 = new Thread(new Thread2()); thread1.start(); TimeUnit.SECONDS.sleep(1); thread2.start(); } }
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-1 release the lock. Thread-0 get the lock.
能够看到当Thread-0调用了wait方法后就释放了object锁。Thread-1获取锁之后调用notify的释放锁,可是这个时候Thread没有立马获取object锁。而是等到了Thread-1的同步块退出之后才获取了object的锁。
假设将class Thread2中的 System.out.println(Thread.currentThread().getName()+" release the lock.");这句放在synchronized(object)的外面。有可能出现例如以下的运行结果:
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-0 get the lock. Thread-1 release the lock.
这是由于,当Thread-1释放了object锁而且退出了同步块之后Thread-0立马获取了锁,而这时候两个线程的打印语句的顺序就随机了。
这个我在以后的博客中会涉及到。