Проблема производителя-потребителя: методы и примеры кода для синхронизации

Проблема производителя-потребителя — это классическая проблема синхронизации в информатике. Он включает в себя два типа потоков, известных как производители и потребители, которые используют общий ресурс, обычно буфер. Производители генерируют данные и добавляют их в буфер, а потребители извлекают и обрабатывают данные из буфера. Цель состоит в том, чтобы гарантировать, что производители и потребители могут работать одновременно без каких-либо конфликтов или повреждения данных.

Существует несколько методов реализации задачи производитель-потребитель. Вот несколько распространенных подходов и примеры кода:

Метод 1: использование синхронизированных методов/блокировок

import java.util.LinkedList;
class Buffer {
    private LinkedList<Integer> buffer = new LinkedList<>();
    private int capacity;
    public Buffer(int capacity) {
        this.capacity = capacity;
    }
    public synchronized void produce(int item) throws InterruptedException {
        while (buffer.size() == capacity) {
            wait();
        }
        buffer.add(item);
        notifyAll();
    }
    public synchronized int consume() throws InterruptedException {
        while (buffer.isEmpty()) {
            wait();
        }
        int item = buffer.remove();
        notifyAll();
        return item;
    }
}
class Producer implements Runnable {
    private Buffer buffer;
    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                buffer.produce(i);
                System.out.println("Produced: " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Consumer implements Runnable {
    private Buffer buffer;
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                int item = buffer.consume();
                System.out.println("Consumed: " + item);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(5);
        Thread producerThread = new Thread(new Producer(buffer));
        Thread consumerThread = new Thread(new Consumer(buffer));
        producerThread.start();
        consumerThread.start();
    }
}

Метод 2: использование BlockingQueue

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Producer implements Runnable {
    private BlockingQueue<Integer> buffer;
    public Producer(BlockingQueue<Integer> buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                buffer.put(i);
                System.out.println("Produced: " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Consumer implements Runnable {
    private BlockingQueue<Integer> buffer;
    public Consumer(BlockingQueue<Integer> buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                int item = buffer.take();
                System.out.println("Consumed: " + item);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        BlockingQueue<Integer> buffer = new LinkedBlockingQueue<>(5);
        Thread producerThread = new Thread(new Producer(buffer));
        Thread consumerThread = new Thread(new Consumer(buffer));
        producerThread.start();
        consumerThread.start();
    }
}

Это всего лишь два примера того, как реализовать проблему производитель-потребитель в Java. Существуют и другие варианты и методы в зависимости от языка программирования и платформы параллелизма, которую вы используете.