公司的一道考试题:设计一个符合生产者和消费者问题的程序。对一个对象(枪膛)进行操作,其最大容量是12颗子弹。生产者线程是一个压入线程,它不断向枪膛中压入子弹;消费者线程是一个射出线程,它不断从枪膛中射出子弹。要求:(1)为了防止两个线程访问一个资源时出现忙等待,要使用的wait-notify函数,是两个线程交替执行;(2)程序输出,要模拟体现对枪膛的压入和射出操作;
1. 任务对象:子弹
public class Task implements Runnable{ private static final Logger LOG = LoggerFactory.getLogger(Task.class); private static AtomicInteger index = new AtomicInteger(0); private int currentIndex; public Task(){ currentIndex = index.incrementAndGet(); } @Override public void run() { LOG.info("子弹" + currentIndex + "执行飞行"); } public String getIndex(){ return String.valueOf(currentIndex); }}
2. 使用wait-notify实现的任务管理容器
public class TaskManager{ private static final Logger LOG = LoggerFactory.getLogger(TaskManager.class); private static final int SIZE = 12; private AtomicInteger count = new AtomicInteger(0); /** * 使用wait-notify及普通list构建个可以阻塞等待的容器。相当于LinkedBlockingQueue */ List taskList = new LinkedList (); public synchronized void put(T task) throws InterruptedException{ while(isFull()){ wait(); } boolean isEmpty = isEmpty(); taskList.add(task); count.incrementAndGet(); /** * 这里加一个isEmpty判断减少唤醒的次数,因为没必要每次put都进行唤醒,只有放入之前是empty的时候才需要唤醒customer. * 如果每次都唤醒,使producer与customer交替执行。size=12也没有意义了。 * 这里put与take等待的其实是两个不同的条件,如果使用Condition来代替内置锁的wait-notify可以更好的降低在锁上面的竞争 */ if(isEmpty){ LOG.info("唤醒消费者线程"); notifyAll(); } } public synchronized T take() throws InterruptedException{ while(isEmpty()){ wait(); } boolean isFull = isFull(); T task =taskList.remove(0); count.decrementAndGet(); if(isFull){ LOG.info("唤醒生产者线程"); notifyAll(); } return task; } private boolean isFull(){ return count.get() == SIZE; } private boolean isEmpty(){ return count.get() == 0; }}
3. 生产者
public class Producer extends Thread{ private static final Logger LOG = LoggerFactory.getLogger(Producer.class); private static AtomicInteger index = new AtomicInteger(0); private TaskManagertaskManager; public Producer(TaskManager taskManager){ this.setName("Thread-Producer-" + index.incrementAndGet()); this.taskManager = taskManager; } @Override public void run(){ while(true){ Task task = new Task(); try { taskManager.put(task); } catch (InterruptedException e) { LOG.info("停止生产"); return; } LOG.info("压入子弹" + task.getIndex()); } }}
4. 消费者
public class Customer extends Thread{ private static final Logger LOG = LoggerFactory.getLogger(Customer.class); private static AtomicInteger index = new AtomicInteger(0); private TaskManagertaskManager; public Customer(TaskManager taskManager){ this.setName("Thread-Customer-" + index.incrementAndGet()); this.taskManager = taskManager; } @Override public void run(){ while(true){ Task task; try { task = taskManager.take(); } catch (InterruptedException e) { LOG.info("停止消费"); //可以将未消费的任务持久化 return; } LOG.info("射出子弹" + task.getIndex()); task.run(); } }}
5. 测试
public class BootStrap { public static void main(String[] args) throws InterruptedException { //可以启动多个Producer及Customer,只要使用的是同一个TaskManager TaskManagertaskManager = new TaskManager (); Producer producer1 = new Producer(taskManager); Producer producer2 = new Producer(taskManager); Customer customer1 = new Customer(taskManager); Customer customer2 = new Customer(taskManager); Customer customer3 = new Customer(taskManager); producer1.start(); producer2.start(); customer1.start(); customer2.start(); customer3.start(); Thread.sleep(1); producer1.interrupt(); producer2.interrupt(); Thread.sleep(5); customer1.interrupt(); customer2.interrupt(); customer3.interrupt(); }}