生产者消费者问题
描述
有一群生产者在生产产品,并将这些产品提供给消费者去消费。为使生产者与消费者能够并发执行,在两者之间设置一个具有 n 个缓冲区的缓冲池,生产者将他所生产的产品放入一个缓冲区中;消费者可从一个缓冲区中取走产品去消费。尽管所有的生产者和消费者都是以异步方式运行,但他们之间必须保持同步,即不允许消费者到一个空缓冲区去取产品;也不允许生产者向一个已装满产品且尚未被取走的缓冲区投放产品。
假设缓冲区为100。
思路
1. 生产者和消费者都有两个状态,生产/消费和等待。
2. 共享资源就是库存,生产者生产产品放入缓冲区(库存增加),消费者消费缓冲区中的产品(库存减少)。
3. 生产者生产产品影响消费者消费(有产品可以消费了),消费者消费产品影响生产者(库存空了可以生产了)。
步骤
1. 设计3个类,一个资源类,一个生产者类,一个消费者类。
资源类
|--(常量)产品仓库大小(缓冲区)
|--(变量)产品仓库
|--(变量)生产的产品编号
|--(变量)消费的产品编号
|--(变量)库存
|--(变量)锁
|--(变量)锁上2个监视器,分别对于生产者和消费者
|--(变量)运行标志
|--(方法)仓库是否已满
|--(方法)仓库是否已空
|--(方法)是否正在运行(返回运行标志)
|--(方法)终止程序运行
|--(方法)生产
|--(方法)消费
生产者类
|--(变量)资源对象
|--(方法)循环生产
消费者类
|--(变量)资源对象
|--(方法)循环消费
2. 生产前判断仓库是否已满,如果已满则等待,不满则生产,并唤醒消费者。
3. 消费前判断仓库是否已空,如果已空则等待,不空则消费,并唤醒生产者。
程序
import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.Condition;class Resource {public static final int MAX = 100;private int front = 0; // 生产的产品编号,生产一个就加1,并打印。private int rear = 0; // 消费的产品编号,消费一个就加1,并打印。private int count = 0; // 仓库中产品的数量。private final Object[] items = new Object[ MAX];private boolean runflag = true;private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();public boolean isFull() // 判断仓库是否满{return ( count == items. length);}public boolean isEmpty() // 判断仓库是否空{return ( count == 0);}public boolean isRunnable() // 生产和消费是否可执行{return runflag;}public void stopRun() // 终止生产和消费{runflag = false;}public void put(Object x) // 生产函数{lock.lock();try {while (isFull()) { // 如果仓库已满,则生产(仓库非满)等待。System.out.println(Thread.currentThread().getName() + " wait!");try {notFull.await();} catch (InterruptedException e) {} // 暂时不做异常处理}items[front] = x;System.out.println(Thread. currentThread().getName() + "...生产 = " + front + "...库存 = "+ ++ count); // 显示线程,生产/消费,指针以及库存。front = (front + 1) % MAX; // 当指针到数组边界后从头开始循环。notEmpty.signal(); // 已经生产出产品,唤醒消费者(仓库非空)。} finally {lock.unlock(); // 锁的释放是必要行为,所以放在 finally 语句中。}}public Object take() // 消费函数{lock.lock();try {// 这里只能使用while而不能使用 if-else 组合,因为 return 语句必须在 try 中,而不可以在 else 中。while (isEmpty()) { // 如果仓库已空,则消费(仓库非空)等待。System.out.println(Thread.currentThread().getName() + " wait!");try {notEmpty.await();} catch (InterruptedException e) {} // 暂时不做异常处理}Object x = items[rear]; // 将指针指向的元素拿出。System.out.println(Thread. currentThread().getName() + "...消费 = " + rear + "...库存 = "+ -- count); // 显示线程,生产/消费,指针以及库存。rear = (rear + 1) % MAX;notFull.signal();return x; // 将拿出的元素给消费者消费。} finally {lock.unlock();}}}class Producer implements Runnable {private Resource r;public Producer(Resource r) // 将公共的资源(仓库)传入构造函数{this.r = r;}public void run() {while ( r.isRunnable()) // 当生产消费可执行时开始生产{r.put(new Object());}}}class Consumer implements Runnable {private Resource r;public Consumer(Resource r) {this.r = r;}public void run() {while ( r.isRunnable()) {r.take();}}}public class ThreadProducerConsumer {public static void main(String[] args) {Resource r = new Resource();Producer p = new Producer( r);Consumer c = new Consumer( r);new Thread( p).start();new Thread( p).start();new Thread( c).start();new Thread( c).start();// 生产消费执行3秒后停止try {Thread.sleep(3000);} catch (InterruptedException e) { /* TODO:处理代码 */}r.stopRun();}}
运行结果(部分)
Thread-1...生产 = 88...库存 = 96
Thread-1...生产 = 89...库存 = 97
Thread-1...生产 = 90...库存 = 98
Thread-1...生产 = 91...库存 = 99
Thread-1...生产 = 92...库存 = 100
Thread-1 wait!
Thread-0 wait!
Thread-3...消费 = 93...库存 = 99
Thread-3...消费 = 94...库存 = 98
Thread-3...消费 = 95...库存 = 97
Thread-3...消费 = 96...库存 = 96
哲学家进餐问题
描述
五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。
思路
1. 哲学家共有两个状态:思考和进餐。只有进餐用到临界资源:筷子。
2. 哲学家使用临界资源有两个方法:使用筷子,放弃筷子,分别对应哲学家两个状态。
3. 哲学家使用筷子始终影响的是邻座的两个哲学家。
步骤
1. 设计两个类,一个筷子类,一个哲学家类。
筷子类
|--(变量)5只筷子
|--(变量)锁
|--(变量)锁上5个监视器方法,分别对应5个哲学家
|--(方法)思考
|--(方法)进餐
哲学家类
|--(变量)筷子类资源
|--(变量)哲学家编号
|--(变量)哲学家工作标志
|--(方法)哲学家工作:包含思考和进餐
|--(方法)哲学家停止工作
|--(方法)run方法:条件循环执行工作方法。
2. 当哲学家进餐,则判断左右两只筷子是否被占用,如果没有,则标记这两只筷子使用,哲学家进餐,进餐人数加1;如果筷子被占用,则哲学家等待。
3. 当哲学家思考,则将左右两只筷子标记未使用,进餐人数减1,唤醒左边和右边两个哲学家。
4. 当哲学家停止工作,那么工作标记将被复位,哲学家工作循环停止。
程序
import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.Condition;class Chopsticks {// 5只筷子使用标记private boolean[] chops = {false, false, false, false, false};private int eatcount = 0; // 进餐人数统计private final Lock lock = new ReentrantLock();private final Condition[] ph_monitor = new Condition[5];{for (int i = 0; i < 5; i++) {// 初始化代码块,初始化针对5个哲学家监视器类ph_monitor[i] = lock.newCondition();}}public void eat( int ph) {lock.lock();try {// 如果左右两只筷子在用,那么该哲学家等待while ( chops[ ph] || chops[( ph + 1) % 5]) {System. out.println("PH." + ph + " Waiting..." );try {ph_monitor[ ph].await();} catch (InterruptedException e) {}}// 左右两只筷子未用,则拿起筷子进餐chops[ph] = true;chops[(ph + 1) % 5] = true;eatcount++;System.out.println( "PH." + ph + "......Eating\tCount = " + eatcount );} finally {lock.unlock();}try {Thread.sleep(5);} catch (InterruptedException e) { /* TODO:处理代码 */}}public void think( int ph) {lock.lock();try {// 哲学家转为思考,则放弃左右两只筷子,并唤醒左右两个哲学家。chops[ph] = false;chops[(ph + 1) % 5] = false;ph_monitor[(ph + 1) % 5].signal();ph_monitor[(ph + 4) % 5].signal();eatcount--; // 进餐人数减1System.out.println( "PH." + ph + "......Thinking\tCount = " + eatcount );} finally {lock.unlock();}try {Thread.sleep(5);} catch (InterruptedException e) { /* TODO:处理代码 */}}}class Philosopher implements Runnable {private int ph = 0;private Chopsticks ch;private boolean runflag = true; // 工作标志,false表示哲学家停止工作Philosopher(int ph, Chopsticks ch) {this.ph = ph;this.ch = ch;}private void work() {// 哲学家工作,包含进餐和思考,并严格交替执行ch.eat(ph);ch.think(ph);}public void run() {while ( runflag) {// 判断工作标志,工作标志为true时,不停工作work();}}public void stopRun() {runflag = false;}}public class ThreadDpp {public static void main(String[] args) {final Chopsticks ch = new Chopsticks();Philosopher ph[] = new Philosopher[5];Thread t[] = new Thread[5];for (int i = 0; i < 5; i++) {ph[i] = new Philosopher( i, ch);t[i] = new Thread( ph[ i]);t[i].start();}// 哲学家工作3秒后停止try {Thread.sleep(3000);} catch (InterruptedException e) { /* TODO:处理代码 */}for (int i = 0; i < 5; i++) {ph[i].stopRun();}}}
运行结果(部分)
PH.0......Eating Count = 1
PH.1 Waiting...
PH.2......Eating Count = 2
PH.3 Waiting...
PH.4 Waiting...
PH.2......Thinking Count = 1
PH.3......Eating Count = 2
PH.1 Waiting...
PH.0......Thinking Count = 1
PH.1......Eating Count = 2
PH.4 Waiting...
PH.3......Thinking Count = 1
PH.0 Waiting...
PH.4......Eating Count = 2
PH.1......Thinking Count = 1
PH.2......Eating Count = 2
PH.0 Waiting...
PH.3 Waiting...
PH.2......Thinking Count = 1
PH.4......Thinking Count = 0
本文详细介绍了生产者消费者问题和哲学家进餐问题的并发解决策略,通过设计资源类、生产者类和消费者类来实现生产与消费的同步与互斥,以及利用锁和条件变量确保线程安全。同时,阐述了哲学家进餐问题的解决方法,包括哲学家的工作状态、临界资源的使用和释放机制,以及如何通过监视器实现多哲学家间的协调与合作。
1645

被折叠的 条评论
为什么被折叠?



