object o = new object();
o.wait();
o.notify();
o.notifyAll();
wait方法和notify(意思为 “通知”)方法均属于object。
当 A线程调用o.wait()时,A会自动放弃CPU并且从Runnable状态变成Waiting状态,o指向对象有一个等待集,A会被调入等待集中。
当B线程调用o.notify();时,会随机唤醒一个等待集上的线程,而o.notifyAll();则会唤醒全部线程,对B本身无影响。
因为以上三个方法均对对象的等待集产生了修改,所以规定在用以上方法前,必须加锁。
synchronized(o){
o.wait() //wait内部会在线程放弃CPU之前将锁解开,当被唤醒后
} // 其会再次请求锁,并执行unlock
synchronized(o){
o.notify()
}
import java.util.Scanner;
/*
单生产者-单消费者情况
1. 是不是线程安全的? 是否有共享,是否有修改?
2. 怎么修改成线程安全的版本? 通过加锁解决
volatile 不可以解决问题
3. 生产者在队列满时等待-消费者在队列空时等待
4. 生产者需要唤醒可能在等消费者;消费者需要唤醒可能在等的生产者
*/
public class MyBlockingArrayQueue {
int[] array = new int[10]; // 下标处的数据可能出现生产者和消费者修改同一处的情况
int front = 0; // 只有消费者修改 front
int rear = 0; // 只有生产者修改 rear
int size = 0; // size 是生产者消费者都会修改的
// 生产者才会调用 put
synchronized void put(int value) throws InterruptedException {
// 考虑满的情况
if (size == array.length) {
// 队列已满
//throw new RuntimeException("队列已满"); //满了
wait(); //停
}
array[rear] = value;
rear++;
if (rear == array.length) {
rear = 0;
}
//rear = (rear + 1) % array.length;
size++; // 我们需要保障的是 size++ 的原子性,所以 volatile 无法解决
notify();
}
// 调用 take 的一定是消费者
synchronized int take() throws InterruptedException {
// 考虑空的情况
if (size == 0) {
// 空的
//throw new RuntimeException("队列已空");
wait();
}
int value = array[front];
front++;
if (front == array.length) {
front = 0;
}
//front = (front + 1) % array.length;
size--;
notify();
return value;
}
static MyBlockingArrayQueue queue = new MyBlockingArrayQueue();
static class Producer extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
System.out.println("准备放入 " + i);
queue.put(i);
System.out.println("放入成功");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Producer producer = new Producer();
producer.start(); //生产者启动
Thread.sleep(2 * 1000);
Scanner scanner = new Scanner(System.in);//消费者手动控制
while (true) {
scanner.nextLine();
System.out.println(queue.take());
}
}
}
synchornized保证了拿的时候不能加,加的时候不能拿。
在take、put方法中的wait、notify,则保证了阻塞,满时加不进,空时取不出,一个线程wait时,只能等待另一个线程调用对应方法末尾的notify来唤醒。
当多对多时,可能会出现生产者唤醒生产者,消费者唤醒消费者的情况,此时会导致线程不安全,因为size在随机变,解决方法就是在线程醒来时再次判断wait的条件,也就是将if换成while。
多对多的还有种情况就是,所有消费者都在wait(队列空时一直都是消费者抢到cpu),然后生产者在队列加满后一直唤醒生产者,于是所有生产者也都在wait,导致整个程序停止下来。解决方法是将notify改成notifyAll。
java中的阻塞队列 ArrayBlockingQueue 顺序表实现
LinkedBlockingQueue 链表实现
PriorityBlockingQueue 堆实现