文章目录
- wait/notify
- api
- wait vs sleep
- 手写消息队列
wait/notify
这块比较简单,就不在把所有例子都写上了。
要注意区分waitSet和EntryList中的线程,一个获得了锁但是wait释放了锁进入等待notify唤醒状态,一个是正在等待获得锁。
不过相同点就是他们都处于阻塞状态,一个是等待阻塞,一个是同步阻塞。 不占用时间片。
- BLOCKED 线程会在 Owner 线程释放锁时唤醒
- WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争
所以只有获取了锁的线程才能wait,如果我们不手动notify,将永远沉睡下去。
api
- obj.wait() 让进入 object 监视器的线程到 waitSet 等待
- obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
- obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
wait可用传入参数,表示等待时间ms,如果到了时间没有被唤醒,那么主动唤醒自己。
它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法。
wait vs sleep
sleep(long n) 和 wait(long n) 的区别
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
- 它们状态 TIMED_WAITING
手写消息队列
操作系统的生产者-消费者模式而已。
生产者:不断判断队列中是否有空余位置,若没有进入等待,若有放入一个产品到队列,然后唤醒消费者消费。
消费者:不断判断队列中是否有产品,若没有进入等待,若有则拿走一个产品,然后唤醒生产者生产品。
package com.消息队列;
import java.util.LinkedList;
public class Test {
}
class MessageQueue {
private final int capacity; // 最大容量
private final LinkedList<Message> dq = new LinkedList<>(); // 存放消息的队列
public MessageQueue(int capacity) {
this.capacity = capacity;
}
/**
* 队列中取出产品
* @return
*/
public Message take(){
synchronized (dq) {
while (dq.isEmpty()) {
try {
dq.wait(); // 若为空,一直等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message first = dq.getFirst(); // 获取产品
dq.notifyAll(); // 唤醒等待dq锁的线程
return first;
}
}
/**
* 队列中放入产品
* @param message
*/
public void put(Message message){
synchronized (dq) {
while (dq.size() >= capacity) {
try {
dq.wait(); // 若无容量了,该线程进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dq.addLast(message); // 放入产品
dq.notifyAll(); // 唤醒等待dq锁的线程
}
}
}
final class Message {
private final int id; // id
private final Object object; // 任务
public Message(int id, Object object) {
this.id = id;
this.object = object;
}
public int getId() {
return id;
}
public Object getObject() {
return object;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", object=" + object +
'}';
}
}
sync已经保证了对于队列操作的互斥,所以其实不wait也可以,一直循环等到线程切换皆可,但是太浪费cpu了。
生产者和消费者都是等待的dq的锁,所以notifyAll的时候,唤醒所有线程,被选中执行的不一定是哪一个,不过如果while条件不符,还是会再wait罢了。