关于wait和notify的使用,在此我们使用链表模拟队列Q,使用synchronized用于实现多线程的同步操作,在特定条件下释放线程持有的锁,然后挂起,等待条件满足进行资源竞争.
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: jaden
* @Date: 2019/5/20 16:44
* @Version 1.0
*/
public class MyQueue {
// 使用链表模拟队列
private final LinkedList<Object> list = new LinkedList<>();
// 计数器(原子操作)
private AtomicInteger count = new AtomicInteger(0);
// 下限初始化为0
private final int minSize = 0;
// 上限初始化为5
private final int maxSize = 5;
private final Object lock = new Object();
public void offer(Object obj) {
synchronized (lock) {
while (count.get() == this.maxSize) {
try {
// 释放掉线程持有的锁并挂起线程
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产
list.offer(obj);
System.out.println("加入数据:" + obj);
count.incrementAndGet();
// 唤醒线程
lock.notify();
}
}
public Object consume() {
Object ret = null;
synchronized (lock){
while (count.get() == this.minSize) {
try {
// 释放掉线程持有的锁并挂起线程
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费
ret = list.remove();
count.decrementAndGet();
// 唤醒线程
lock.notify();
}
return ret;
}
public int getSize() {
return this.list.size();
}
public static void main(String[] args) {
MyQueue mq = new MyQueue();
mq.offer("1");
mq.offer("2");
mq.offer("3");
mq.offer("4");
mq.offer("5");
System.out.println("当前Q长度:" + mq.getSize());
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
mq.offer("6");
}
},"线程T1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
Object o1 = mq.consume();
System.out.println("移除数据:" + o1);
}
},"线程T2");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
wait与notify为什么要放在synchronized代码块内?
下面我们看下wait()方法到底做了什么:
调用了wait()后, 当前线程会释放该对象持有的锁, 然后当前线程会被添加到该对象的等待队列上, 等待条件再次参与锁竞争。 因为wait()方法会强迫线程首先进行释放锁操作,所以wait()方法应该先获取锁,这就解答了wait为什么要放在synchronized代码块内。
notify()与wait()成对使用,当一个线程调用一个对象的notify()方法时,调度器会从等待队列中任意取出一个线程, 将其添加到入口队列中。入口队列中的多个线程就会竞争对象的锁, 得到锁的线程就可以继续执行, 如果等待队列中没有线程,notify()方法也不会抛出异常。
notifyAll()会将等待队列中所有的线程都唤醒,因此在实际中 notifyAll()比notify()更常用 。