wait && notify 的两个应用
1.模式之保护性暂停
用在一个线程等待另外一个线程的执行结果
要点:
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
- 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
- JDK 中,join 的实现、Future 的实现,采用的就是此模式
实现:
class GuardedObject {
private Object response;
private final Object lock = new Object();
public Object get() {
synchronized (lock) {
// 条件不满足则等待
while (response == null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
}
public void complete(Object response) {
synchronized (lock) {
// 条件满足,通知等待线程
this.response = response;
lock.notifyAll();
}
}
}
2.模式之生产者消费者
- 与7保护性暂停中的 GuardObject 不同,不需要产生结果和消费结果的线程一一对应
- 消费队列可以用来平衡生产和消费的线程资源
- 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
- 消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据
- JDK 中各种阻塞队列,采用的就是这种模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A2dAwvKE-1612170647754)(E:\系统盘_桌面\markdown\image-20210201165055891.png)]
实现:
//消息队列类 java线程之间通信
class MessageQueue {
private LinkedList<Message> queue = new LinkedList<>();
private int capacity;
public MessageQueue(int capacity) {
this.capacity = capacity;
}
//获取消息
public Message take() {
//检查队列是否为空
synchronized (queue) {
while (queue.isEmpty()) {
try {
System.out.println("队列为空,消费者线程等待");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message message = queue.removeFirst();
System.out.println("已消费消息"+message);
queue.notifyAll();
return message;
}
}
//存入消息
public void put(Message message) {
synchronized (queue) {
while (queue.size() == capacity) {
try {
System.out.println("队列已满,生产者线程等待");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.addLast(message);
System.out.println("已生产消息:"+message);
queue.notifyAll();
}
}
}
final class Message{
private int id;
private Object object;
public Message(int id, Object object) {
this.id = id;
this.object = object;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", object=" + object +
'}';
}
public int getId() {
return id;
}
public Object getObject() {
return object;
}
}
//测试
public static void main(String[] args) {
MessageQueue messageQueue = new MessageQueue(2);
for (int i = 0; i < 3; i++) {
int id =i;
new Thread(() -> {
messageQueue.put(new Message(id,"值:"+id));
}, "生产者" + i).start();
}
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message take = messageQueue.take();
}
}, "消费者").start();
}