1、object - wait、notify、notifyAll
wait、notify、notifyAll是object中的方法,final修饰,因此不能被重写。
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException {
wait(0);
}
wait、notify、notifyAll三个方法调用的前提是已经获取到当前实例对象锁,也就是在synchroized作用范围下。
1.1、wait
1、wait的作用是让当前获取了对象锁的线程释放对象锁,并进入阻塞状态,除非发生下列四种中的某一种情况,否则线程会一直在处于阻塞状态。
(1)有其他线程调用了object的notify方法来唤醒线程;
(2)有其他线程调用了object的notifyAll方法来唤醒线程;
(3)有其他线程调用了被阻塞线程thread的interrupt方法来唤醒被阻塞线程。
(4)wait方法超时,该线程继续运行。
线程被重新唤醒后,wait后面的代码不会继续执行,线程会重新获取对象锁进入synchronized代码块。
2、如果当前线程没有持有对象锁的情况下,调用了object的wait,会抛出IllegalMonitorStateException异常。
3、当前线程调用object的wait后,如果被中断的话,InterruptedException会抛出。
1.2、notify
notify的作用是唤醒一个等待object对象锁的线程,待当前线程退出synchronized代码块释放对象锁后,被唤醒的线程才能获得对象锁。如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
如果当前线程并没有持有object的对象锁,但调用了object的notify,会抛出IllegalMonitorStateException异常。
1.3、notifyAll
notifyAll的作用是唤醒所有正在等待对象锁的线程,待当前线程退出synchronized代码块释放对象锁后,被唤醒的线程才能获得对象锁。如果当前线程并没有持有object的对象锁,但调用了object的notify,会抛出IllegalMonitorStateException异常。
1.4、生产者、消费者模型
使用wait、notify实现:
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
synchronized (queue) {
while(queue.size() == 0){
try {
System.out.println("队列空,等待数据");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.poll(); //每次移走队首元素
queue.notify();
System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
synchronized (queue) {
while(queue.size() == queueSize){
try {
System.out.println("队列满,等待有空余空间");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(1); //每次插入一个元素
queue.notify();
System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
}
}
}
}
}
2、condition - await、signal、signalAll
wait、signal、signalAll是Condition中的方法,Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition() ,调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用。
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
使用condition实现生产者、消费者模型:
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
lock.lock();
try {
while(queue.size() == 0){
try {
System.out.println("队列空,等待数据");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll(); //每次移走队首元素
notFull.signal();
System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
} finally{
lock.unlock();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
lock.lock();
try {
while(queue.size() == queueSize){
try {
System.out.println("队列满,等待有空余空间");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1); //每次插入一个元素
notEmpty.signal();
System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
} finally{
lock.unlock();
}
}
}
}
}
3、object和condition两种线程协作方式比较
object的协作方式,因为只锁定一个object对象,因此只能有一个等待队列,在实际应用时可能需要多个,比如读和写。为了这个灵活性,lock将同步控制和等待队列分离开来,lock对象负责同步控制,保证在某个时刻只有一个线程访问临界区。等待队列中存储着被阻塞的线程,每个condition对应一个等待队列,而一个lock可以创建多个condition。
举例说明如下:
线程A:
synchronized(obj){
obj.wait();
doSomething();
}
线程C:
synchronized(obj){
obj.wait();
doSomething();
}
线程B:
synchronized(obj){
obj.notify();
}
假设三个线程执行顺序,线程A-->线程C-->线程B
所以问题来了:等待的线程中有A和C, B notify后只会唤醒其中一个执行,假如我们的需求是想让A线程执行,那么这种object的方式是无法控制的。用一个lock和两个condition就可以解决。
ReentrantLock lock = new ReentrantLock(true);
Condition aCondition = reentrantLock.newCondition();
Condition cCondition = reentrantLock.newCondition();
线程A:
{
lock.lock();
aCondition.await();
aCondition.signal();
doSomething();
lock.unlock();
}
线程C:
{
lock.lock();
cCondition.await();
doSomething();
lock.unlock();
}
线程B:
{
lock.lock();
aCondition.signal();
//cCondition.signal();
lock.unlock();
}
可见condition比object更灵活。