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();
    }
}

界面截图:

ProducerConsumer

代码解释:

  1. 缓冲区: 使用 buffer 数组表示缓冲区,count 变量记录缓冲区中已有的数据数量。
  2. 互斥锁: 使用 lock 对象作为互斥锁,确保同一时刻只有一个线程访问缓冲区。
  3. 生产者线程: 生产者线程循环执行以下操作:
    • 睡眠一段时间,模拟生产数据需要的时间。
    • 获取互斥锁。
    • 如果缓冲区已满,则等待消费者取出数据。
    • 向缓冲区添加数据。
    • 更新缓冲区状态,并在界面上显示。
    • 释放互斥锁,唤醒等待的消费者线程。
  4. 消费者线程: 消费者线程循环执行以下操作:
    • 睡眠一段时间,模拟消费数据需要的时间。
    • 获取互斥锁。
    • 如果缓冲区为空,则等待生产者添加数据。
    • 从缓冲区取出数据。
    • 更新缓冲区状态,并在界面上显示。
    • 释放互斥锁,唤醒等待的生产者线程。
  5. 界面: 使用 Swing 创建简单的图形化界面,显示生产者和消费者的睡眠时间,以及缓冲区的状态。

总结:

本示例演示了使用 synchronized 关键字实现生产者-消费者问题。synchronized 关键字可以保证线程安全,并提供了一种简单的同步机制,通过 wait()notifyAll() 方法实现线程间的协作。该示例也展示了使用 Swing 创建图形化界面的方法,方便观察程序的运行状态。

注意:

本示例只是一个简单的实现,在实际应用中,可能需要考虑更复杂的场景,例如使用 BlockingQueue 或其他同步机制来提高效率和安全性。

Java 多线程生产者-消费者问题:使用 synchronized 实现同步和互斥

原文地址: https://www.cveoy.top/t/topic/nG5R 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录