这是个比较基础但是面试常考的一个问题,用两道经典面试题来close这个话题吧。在Java中实现线程协作一般就是用wait/notify模式。
一.两个线程交替打印0~100的奇偶数
/*
* 简单复习:
* 1.wait和notify都是Object类的方法。
* 2.wait和notify必须要在synchronized代码块中执行,否则会抛异常。
*/
public class WaitNotifyPrint {
private static int count = 0;
//两个线程竞争该对象锁
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(new TurningRunner(), "偶数").start();
new Thread(new TurningRunner(), "奇数").start();
}
//1. 拿到锁直接就打印。
//2. 打印完,唤醒其他线程,自己就休眠。
static class TurningRunner implements Runnable {
@Override
public void run() {
while (count <= 100) {
synchronized (lock) {
//拿到锁就打印
System.out.println(Thread.currentThread().getName() + ":" + count++);
//打印完,唤醒其他线程
lock.notify();
//如果任务还没结束,就调用wait()让出当前的锁
if (count <= 100) {
try {
//自己休眠
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
注意交替打印奇偶数还可以用synchronized实现,感兴趣可以自行百度~
二.实现经典的生产者消费者模式
/*
* 场景描述:用EventStorage模拟仓库,Producer代表生产者,Consumer代表消费者,
* 生产者和消费者共同对仓库进行协作。
* 下面会有大量注释进行说明,放心阅读~
*/
public class ProducerConsumerModel {
public static void main(String[] args) {
//初始化仓库
EventStorage eventStorage = new EventStorage();
//用仓库初始化生产者
Producer producer = new Producer(eventStorage);
//用仓库初始化消费者
Consumer consumer = new Consumer(eventStorage);
//启动生产者和消费者
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Producer implements Runnable {
private EventStorage storage;
public Producer(
EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
//生产者往仓库中生产100个对象(此时消费者也在消费)
for (int i = 0; i < 100; i++) {
storage.put();
}
}
}
class Consumer implements Runnable {
private EventStorage storage;
public Consumer(
EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
//消费者循环消费仓库中的对象(此时生产者也在生产)
for (int i = 0; i < 100; i++) {
storage.take();
}
}
}
class EventStorage {
private int maxSize;
//用Date来模拟对象
private LinkedList<Date> storage;
//注意看构造方法定义仓库大小为10,并且用LinkedList来存储对象
public EventStorage() {
maxSize = 10;
storage = new LinkedList<>();
}
public synchronized void put() {
//仓库满了就休眠
while (storage.size() == maxSize) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库没满就添加并通知消费者
storage.add(new Date());
System.out.println("仓库里有了" + storage.size() + "个产品。");
notify();
}
public synchronized void take() {
//仓库空了就休眠
while (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没空就取数据,poll方法代表检索并删除并通知生产者
System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
notify();
}
}
三.总结
wait/notify是比较底层的实现,现在一般都是用JDK封装好的一些工具类或框架,比如阻塞队列,线程协作工具类。但是这些工具万变不离其宗,了解本质才是根本。