文章目录
- ❌ TODO:本文仅供参考,极有可能有误
- 1.生产者消费者问题(信号量)
- 💖 ProducerConsumerExample.java
- 🏆 运行结果
- 💖 ProducerConsumerSelectiveExample.java
- 🏆 运行结果
- 2.实现睡觉的理发师问题
- 💖 BarberShop.java
- 🏆 运行结果
❌ TODO:本文仅供参考,极有可能有误
1.生产者消费者问题(信号量)
参考教材中的生产者消费者算法,创建5个进程,其中两个进程为生产者进程,3个进程为消费者进程。一个生产者进程试图不断地在一个缓冲中写入大写字母,另一个生产者进程试图不断地在缓冲中写入小写字母。3个消费者不断地从缓冲中读取一个字符并输出。为了使得程序的输出易于看到结果,仿照的实例程序,分别在生产者和消费者进程的合适的位置加入一些随机睡眠时间。
💖 ProducerConsumerExample.java
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ProducerConsumerExample {
// 定义缓冲区的大小为10
private static final int BUFFER_SIZE = 10;
// 创建一个阻塞队列,用于存放字符,大小为BUFFER_SIZE
private static final BlockingQueue<Character> buffer = new ArrayBlockingQueue<>(BUFFER_SIZE);
// 定义生产者类,继承自Thread
static class Producer extends Thread {
// 是否生成大写字母
private final boolean uppercase;
// 随机数生成器
private final Random random = new Random();
// 构造函数,接收一个布尔值,决定生成大写还是小写字母
public Producer(boolean uppercase) {
this.uppercase = uppercase;
}
// 重写run方法,定义生产者的行为
@Override
public void run() {
try {
// 循环直到线程被中断
while (!Thread.currentThread().isInterrupted()) {
// 根据是否生成大写字母,生成随机字符
char item = uppercase ? Character.toUpperCase((char) ('A' + random.nextInt(26))) :
Character.toLowerCase((char) ('a' + random.nextInt(26)));
// 将生成的字符放入缓冲区
buffer.put(item);
// 打印生产信息
System.out.println("Produced " + item + " by " + this.getName());
// 随机休眠一段时间
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
// 捕获InterruptedException异常,并重新中断当前线程
Thread.currentThread().interrupt();
}
}
}
// 定义消费者类,继承自Thread
static class Consumer extends Thread {
// 随机数生成器
private final Random random = new Random();
// 重写run方法,定义消费者的行为
@Override
public void run() {
try {
// 循环直到线程被中断
while (!Thread.currentThread().isInterrupted()) {
// 从缓冲区取出一个字符
char item = (char) buffer.take();
// 打印消费信息
System.out.println("Consumed " + item + " by " + this.getName());
// 随机休眠一段时间
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
// 捕获InterruptedException异常,并重新中断当前线程
Thread.currentThread().interrupt();
}
}
}
// 主函数,程序的入口点
public static void main(String[] args) {
// 创建两个生产者线程,一个生成大写字母,一个生成小写字母
Producer producer1 = new Producer(true);
Producer producer2 = new Producer(false);
// 创建三个消费者线程
Consumer consumer1 = new Consumer();
Consumer consumer2 = new Consumer();
Consumer consumer3 = new Consumer();
// 启动所有生产者和消费者线程
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
consumer3.start();
try {
// 等待所有线程完成
producer1.join();
producer2.join();
consumer1.join();
consumer2.join();
consumer3.join();
} catch (InterruptedException e) {
// 打印异常信息
e.printStackTrace();
}
}
}
可选的实验:在上面实验的基础上实现部分消费者有选择地消费某些产品。例如一个消费者只消费小写字符,一个消费者只消费大写字母,而另一个消费者则无选择地消费任何产品。消费者要消费的产品没有时,消费者进程被阻塞。注意缓冲的管理。
🏆 运行结果
💖 ProducerConsumerSelectiveExample.java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class ProducerConsumerSelectiveExample {
private static final int BUFFER_SIZE = 10;
private static final BlockingQueue<Character> buffer = new ArrayBlockingQueue<>(BUFFER_SIZE);
static class Producer extends Thread {
private final boolean uppercase;
private final Random random = new Random();
public Producer(boolean uppercase) {
this.uppercase = uppercase;
}
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
char item = uppercase ? Character.toUpperCase((char) ('A' + random.nextInt(26))) :
Character.toLowerCase((char) ('a' + random.nextInt(26)));
buffer.put(item);
System.out.println("Produced " + item + " by " + this.getName());
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
static class Consumer extends Thread {
private final boolean onlyUppercase;
private final Random random = new Random();
public Consumer(boolean onlyUppercase) {
this.onlyUppercase = onlyUppercase;
}
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Character item = buffer.poll(100, TimeUnit.MILLISECONDS); // 等待最多100毫秒
if (item ==null) {
// 如果没有找到想要的产品,跳过此次循环
continue;
}
// 根据消费者类型进行选择性消费
boolean consume = onlyUppercase ? Character.isUpperCase(item) :
!onlyUppercase || Character.isLowerCase(item);
if (consume) {
buffer.remove(item); // 从队列中移除消费的项
System.out.println("Consumed " + item + " by " + this.getName());
}
// 这里不需要 else 块,因为如果 consume 为 false,item 已经被检查过不是所需类型
// 并且已经被忽略,不需要再次检查或睡眠
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
Producer producer1 = new Producer(true);
Producer producer2 = new Producer(false);
Consumer consumerUppercaseOnly = new Consumer(true); // 只消费大写字母
Consumer consumerLowercaseOnly = new Consumer(false); // 只消费小写字母
Consumer consumerAny = new Consumer(true); // 消费任何产品
producer1.start();
producer2.start();
consumerUppercaseOnly.start();
consumerLowercaseOnly.start();
consumerAny.start();
try {
producer1.join();
producer2.join();
consumerUppercaseOnly.join();
consumerLowercaseOnly.join();
consumerAny.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
🏆 运行结果
2.实现睡觉的理发师问题
(同步互斥方式采用信号量或mutex方式均可)
理发师问题的描述:一个理发店接待室有n张椅子,工作室有1张椅子;没有顾客时,理发师睡觉;第一个顾客来到时,必须将理发师唤醒;顾客来时如果还有空座的话,他就坐在一个座位上等待;如果顾客来时没有空座位了,他就离开,不理发了;当理发师处理完所有顾客,而又没有新顾客来时,他又开始睡觉。
💖 BarberShop.java
import java.util.concurrent.Semaphore;
import java.util.Random;
public class BarberShop {
private final int capacity;
private final Semaphore barber = new Semaphore(1);
private final Semaphore chair = new Semaphore(0);
private final Semaphore[] seats;
private final Random random = new Random();
public BarberShop(int capacity) {
this.capacity = capacity;
this.seats = new Semaphore[capacity];
for (int i = 0; i < capacity; i++) {
this.seats[i] = new Semaphore(1);
}
}
public void barberAction() {
try {
while (!Thread.currentThread().isInterrupted()) {
barber.acquire();
chair.acquire(); // 理发师等待顾客坐下
System.out.println("理发师醒来开始理发。");
// 模拟随机理发时间
Thread.sleep(random.nextInt(1000) + 100);
System.out.println("理发师完成理发。");
barber.release(); // 理发师完成服务,可以服务下一个顾客
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void customerAction() {
try {
while (!Thread.currentThread().isInterrupted()) {
boolean foundSeat = false;
for (Semaphore seat : seats) {
if (seat.tryAcquire()) {
foundSeat = true;
System.out.println("顾客坐在椅子上等待理发。");
chair.release(); // 唤醒理发师
// 模拟理发师服务时间
Thread.sleep(random.nextInt(1000) + 100);
System.out.println("顾客离开理发椅。");
seat.release(); // 顾客离开,释放椅子
break;
}
}
if (!foundSeat) {
System.out.println("没有空椅子,顾客离开。");
}
// 模拟随机顾客到来时间
Thread.sleep(random.nextInt(1000) + 500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
final int CAPACITY = 3; // 假设有3张椅子
BarberShop shop = new BarberShop(CAPACITY);
Thread barberThread = new Thread(() -> shop.barberAction());
barberThread.start();
// 创建顾客线程
Thread[] customerThreads = new Thread[5];
for (int i = 0; i < customerThreads.length; i++) {
customerThreads[i] = new Thread(() -> shop.customerAction());
customerThreads[i].start();
}
}
}