Java生产者-消费者问题:线程同步、互斥与图形化界面实现
生产者-消费者问题是一个经典的多线程同步问题,其中生产者线程负责生产数据并将其放入缓冲区,而消费者线程则负责从缓冲区中取出数据并进行消费。在多线程环境下,需要使用同步和互斥的技术保证数据的正确性和线程的安全性。
本文将介绍一种使用Java语言实现生产者-消费者问题的方法,并提供一个基于Java Swing的图形化界面,方便用户交互。
1. 缓冲区类
public class Buffer {
private final int size; // 缓冲区大小
private final Object[] buffer; // 缓冲区
private int in; // 生产者索引
private int out; // 消费者索引
private int count; // 当前缓冲区中的数据量
private final Lock lock; // 锁对象
private final Condition notFull; // 缓冲区未满条件变量
private final Condition notEmpty; // 缓冲区非空条件变量
public Buffer(int size) {
this.size = size;
this.buffer = new Object[size];
this.in = 0;
this.out = 0;
this.count = 0;
this.lock = new ReentrantLock();
this.notFull = lock.newCondition();
this.notEmpty = lock.newCondition();
}
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (count == size) { // 缓冲区已满,等待消费者消费
notFull.await();
}
buffer[in] = item; // 将数据放入缓冲区
in = (in + 1) % size; // 生产者索引加一
count++; // 数据量加一
notEmpty.signal(); // 通知消费者可以消费了
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) { // 缓冲区为空,等待生产者生产
notEmpty.await();
}
Object item = buffer[out]; // 从缓冲区取出数据
out = (out + 1) % size; // 消费者索引加一
count--; // 数据量减一
notFull.signal(); // 通知生产者可以生产了
return item;
} finally {
lock.unlock();
}
}
}
2. 生产者类
public class Producer implements Runnable {
private final Buffer buffer;
private final int id;
public Producer(Buffer buffer, int id) {
this.buffer = buffer;
this.id = id;
}
@Override
public void run() {
try {
while (true) {
Object item = produce();
buffer.put(item);
System.out.println('Producer ' + id + ' produced ' + item);
Thread.sleep((long) (Math.random() * 1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Object produce() {
return id + '-' + System.currentTimeMillis();
}
}
3. 消费者类
public class Consumer implements Runnable {
private final Buffer buffer;
private final int id;
public Consumer(Buffer buffer, int id) {
this.buffer = buffer;
this.id = id;
}
@Override
public void run() {
try {
while (true) {
Object item = buffer.take();
consume(item);
System.out.println('Consumer ' + id + ' consumed ' + item);
Thread.sleep((long) (Math.random() * 1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void consume(Object item) {
// do something
}
}
4. 图形化界面类
public class MainFrame extends JFrame {
private static final long serialVersionUID = 1L;
private final Buffer buffer;
private final List<Producer> producers;
private final List<Consumer> consumers;
public MainFrame() {
super('Producer-Consumer Problem');
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
// 创建缓冲区、生产者和消费者
buffer = new Buffer(10);
producers = new ArrayList<>();
consumers = new ArrayList<>();
for (int i = 0; i < 3; i++) {
producers.add(new Producer(buffer, i));
consumers.add(new Consumer(buffer, i));
}
// 创建按钮
JButton startButton = new JButton('Start');
startButton.addActionListener(e -> start());
JButton stopButton = new JButton('Stop');
stopButton.addActionListener(e -> stop());
JPanel buttonPanel = new JPanel();
buttonPanel.add(startButton);
buttonPanel.add(stopButton);
// 添加组件
add(buttonPanel, BorderLayout.NORTH);
JTextArea logArea = new JTextArea();
add(new JScrollPane(logArea), BorderLayout.CENTER);
// 启动生产者和消费者线程
start();
// 输出日志
Runnable logger = () -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(() -> logArea.setText(getLog()));
}
};
new Thread(logger).start();
}
private String getLog() {
StringBuilder sb = new StringBuilder();
sb.append('Buffer: ');
for (int i = 0; i < buffer.getSize(); i++) {
if (i == buffer.getIn() && i == buffer.getOut()) {
sb.append('[I/O]');
} else if (i == buffer.getIn()) {
sb.append('[IN]');
} else if (i == buffer.getOut()) {
sb.append('[OUT]');
} else {
sb.append('[ ]');
}
sb.append(buffer.getBuffer()[i] == null ? ' ' : '*');
}
sb.append('\n');
sb.append('Producers: ');
for (Producer producer : producers) {
sb.append(producer.isRunning() ? '+ ' : '- ');
}
sb.append('\n');
sb.append('Consumers: ');
for (Consumer consumer : consumers) {
sb.append(consumer.isRunning() ? '+ ' : '- ');
}
sb.append('\n');
return sb.toString();
}
private void start() {
for (Producer producer : producers) {
new Thread(producer).start();
producer.setRunning(true);
}
for (Consumer consumer : consumers) {
new Thread(consumer).start();
consumer.setRunning(true);
}
}
private void stop() {
for (Producer producer : producers) {
producer.setRunning(false);
}
for (Consumer consumer : consumers) {
consumer.setRunning(false);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true));
}
}
5. 运行效果

注意:
- 由于多线程的随机性,程序的运行结果可能会有所不同。
- 本实现为了简化代码,并没有对异常情况进行处理,因此在实际应用中需要进行适当的处理。
希望本文能够帮助您理解并实现生产者-消费者问题。
原文地址: https://www.cveoy.top/t/topic/nG6B 著作权归作者所有。请勿转载和采集!