Java 多线程生产者-消费者问题:使用 synchronized 实现同步和互斥
Java 多线程生产者-消费者问题:使用 synchronized 实现同步和互斥
本示例演示了在 Java 中使用多线程实现经典的生产者-消费者问题。通过 synchronized 关键字保证线程安全,并使用 Java Swing 创建简单的图形化界面,展示缓冲区的变化。
问题描述:
生产者和消费者共享一个有限大小的缓冲区。生产者向缓冲区中添加数据,消费者从缓冲区中取出数据。为了避免生产者在缓冲区已满时继续添加数据,或者消费者在缓冲区为空时尝试取出数据,需要使用同步机制来协调生产者和消费者的操作。
代码实现:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ProducerConsumer {
private static final int BUFFER_SIZE = 10;
private static final Object lock = new Object();
private static int[] buffer = new int[BUFFER_SIZE];
private static int count = 0;
private static JLabel labelBuffer;
public static void main(String[] args) {
JFrame frame = new JFrame('Producer-Consumer Problem');
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setLayout(new BorderLayout());
JPanel panelTop = new JPanel();
panelTop.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel labelProducer = new JLabel('Producer: ');
JTextField textFieldProducer = new JTextField(5);
JLabel labelConsumer = new JLabel('Consumer: ');
JTextField textFieldConsumer = new JTextField(5);
JButton buttonStart = new JButton('Start');
panelTop.add(labelProducer);
panelTop.add(textFieldProducer);
panelTop.add(labelConsumer);
panelTop.add(textFieldConsumer);
panelTop.add(buttonStart);
JPanel panelBottom = new JPanel();
panelBottom.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel labelBufferText = new JLabel('Buffer: ');
labelBuffer = new JLabel(printBuffer());
panelBottom.add(labelBufferText);
panelBottom.add(labelBuffer);
frame.add(panelTop, BorderLayout.NORTH);
frame.add(panelBottom, BorderLayout.CENTER);
buttonStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int producerSleepTime = Integer.parseInt(textFieldProducer.getText());
int consumerSleepTime = Integer.parseInt(textFieldConsumer.getText());
startProducerConsumer(producerSleepTime, consumerSleepTime);
}
});
frame.setVisible(true);
}
private static void startProducerConsumer(int producerSleepTime, int consumerSleepTime) {
Thread producerThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(producerSleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (count == BUFFER_SIZE) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
buffer[count++] = 1;
labelBuffer.setText(printBuffer());
lock.notifyAll();
}
}
}
});
Thread consumerThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(consumerSleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (count == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
buffer[--count] = 0;
labelBuffer.setText(printBuffer());
lock.notifyAll();
}
}
}
});
producerThread.start();
consumerThread.start();
}
private static String printBuffer() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < BUFFER_SIZE; i++) {
sb.append(buffer[i]);
sb.append(' ');
}
return sb.toString();
}
}
界面截图:

代码解释:
- 缓冲区: 使用
buffer数组表示缓冲区,count变量记录缓冲区中已有的数据数量。 - 互斥锁: 使用
lock对象作为互斥锁,确保同一时刻只有一个线程访问缓冲区。 - 生产者线程: 生产者线程循环执行以下操作:
- 睡眠一段时间,模拟生产数据需要的时间。
- 获取互斥锁。
- 如果缓冲区已满,则等待消费者取出数据。
- 向缓冲区添加数据。
- 更新缓冲区状态,并在界面上显示。
- 释放互斥锁,唤醒等待的消费者线程。
- 消费者线程: 消费者线程循环执行以下操作:
- 睡眠一段时间,模拟消费数据需要的时间。
- 获取互斥锁。
- 如果缓冲区为空,则等待生产者添加数据。
- 从缓冲区取出数据。
- 更新缓冲区状态,并在界面上显示。
- 释放互斥锁,唤醒等待的生产者线程。
- 界面: 使用
Swing创建简单的图形化界面,显示生产者和消费者的睡眠时间,以及缓冲区的状态。
总结:
本示例演示了使用 synchronized 关键字实现生产者-消费者问题。synchronized 关键字可以保证线程安全,并提供了一种简单的同步机制,通过 wait() 和 notifyAll() 方法实现线程间的协作。该示例也展示了使用 Swing 创建图形化界面的方法,方便观察程序的运行状态。
注意:
本示例只是一个简单的实现,在实际应用中,可能需要考虑更复杂的场景,例如使用 BlockingQueue 或其他同步机制来提高效率和安全性。
原文地址: https://www.cveoy.top/t/topic/nG5R 著作权归作者所有。请勿转载和采集!