生产者-消费者问题是一个经典的多线程同步问题,其中生产者线程负责生产数据并将其放入缓冲区,而消费者线程则负责从缓冲区中取出数据并进行消费。在多线程环境下,需要使用同步和互斥的技术保证数据的正确性和线程的安全性。

本文将介绍一种使用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. 运行效果

Producer-Consumer Problem

注意:

  • 由于多线程的随机性,程序的运行结果可能会有所不同。
  • 本实现为了简化代码,并没有对异常情况进行处理,因此在实际应用中需要进行适当的处理。

希望本文能够帮助您理解并实现生产者-消费者问题。


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

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