[学习笔记]Java多线程经典问题

生产者消费者问题

描述

有一群生产者在生产产品,并将这些产品提供给消费者去消费。为使生产者与消费者能够并发执行,在两者之间设置一个具有 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--; // 进餐人数减1
System.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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值