Solución productor-consumidor usando hilos en Java

En informática, el problema del productor-consumidor (también conocido como problema del búfer acotado) es un ejemplo clásico de un problema de sincronización de procesos múltiples. El problema describe dos procesos, el productor y el consumidor, que comparten un búfer común de tamaño fijo que se utiliza como cola. 

  • El trabajo del productor es generar datos, ponerlos en el búfer y comenzar de nuevo.
  • Al mismo tiempo, el consumidor consume los datos (es decir, los elimina del búfer), una pieza a la vez.

Problema 
Para asegurarse de que el productor no intente agregar datos en el búfer si está lleno y que el consumidor no intente eliminar datos de un búfer vacío.

Solución 
El productor debe ir a dormir o descartar datos si el búfer está lleno. La próxima vez que el consumidor retire un artículo del búfer, notifica al productor, quien comienza a llenar el búfer nuevamente. De la misma manera, el consumidor puede irse a dormir si encuentra que el búfer está vacío. La próxima vez que el productor pone datos en el búfer, despierta al consumidor dormido. 
Una solución inadecuada podría resultar en un punto muerto en el que ambos procesos esperan ser activados. 
Lectura recomendada: subprocesos múltiples en JAVA , sincronizados en JAVA , comunicación entre subprocesos

Implementación de la Clase Consumidor Productor 

  • Una lista LinkedList : para almacenar una lista de trabajos en cola.
  • Una capacidad variable : para verificar si la lista está llena o no
  • Un mecanismo para controlar la inserción y extracción de esta lista para que no insertemos en la lista si está llena o la eliminemos si está vacía.

Nota: Se recomienda probar el siguiente programa en un IDE sin conexión, ya que los bucles infinitos y el método de suspensión pueden hacer que se agote el tiempo de espera en cualquier IDE en línea.  

Java

// Java program to implement solution of producer
// consumer problem.
 
import java.util.LinkedList;
 
public class Threadexample {
    public static void main(String[] args)
        throws InterruptedException
    {
        // Object of a class that has both produce()
        // and consume() methods
        final PC pc = new PC();
 
        // Create producer thread
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                try {
                    pc.produce();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
 
        // Create consumer thread
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                try {
                    pc.consume();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
 
        // Start both threads
        t1.start();
        t2.start();
 
        // t1 finishes before t2
        t1.join();
        t2.join();
    }
 
    // This class has a list, producer (adds items to list
    // and consumer (removes items).
    public static class PC {
 
        // Create a list shared by producer and consumer
        // Size of list is 2.
        LinkedList<Integer> list = new LinkedList<>();
        int capacity = 2;
 
        // Function called by producer thread
        public void produce() throws InterruptedException
        {
            int value = 0;
            while (true) {
                synchronized (this)
                {
                    // producer thread waits while list
                    // is full
                    while (list.size() == capacity)
                        wait();
 
                    System.out.println("Producer produced-"
                                       + value);
 
                    // to insert the jobs in the list
                    list.add(value++);
 
                    // notifies the consumer thread that
                    // now it can start consuming
                    notify();
 
                    // makes the working of program easier
                    // to  understand
                    Thread.sleep(1000);
                }
            }
        }
 
        // Function called by consumer thread
        public void consume() throws InterruptedException
        {
            while (true) {
                synchronized (this)
                {
                    // consumer thread waits while list
                    // is empty
                    while (list.size() == 0)
                        wait();
 
                    // to retrieve the first job in the list
                    int val = list.removeFirst();
 
                    System.out.println("Consumer consumed-"
                                       + val);
 
                    // Wake up producer thread
                    notify();
 
                    // and sleep
                    Thread.sleep(1000);
                }
            }
        }
    }
}

Producción: 

Producer produced-0
Producer produced-1
Consumer consumed-0
Consumer consumed-1
Producer produced-2

Puntos importantes  

  • En la clase PC (una clase que tiene métodos de producción y consumo), se agrega una lista vinculada de trabajos y una capacidad de la lista para verificar que el productor no produce si la lista está llena.
  • En la clase Producer , el valor se inicializa como 0. 
    • Además, tenemos un bucle exterior infinito para insertar valores en la lista. Dentro de este ciclo, tenemos un bloque sincronizado para que solo se ejecute un subproceso productor o consumidor a la vez.
    • Hay un bucle interno antes de agregar los trabajos a la lista que verifica si la lista de trabajos está llena, el subproceso productor abandona el bloqueo intrínseco en la PC y pasa al estado de espera.
    • Si la lista está vacía, el control pasa por debajo del ciclo y agrega un valor en la lista.
  • En la clase Consumer , nuevamente tenemos un bucle infinito para extraer un valor de la lista.
    • En el interior, también tenemos un bucle interno que comprueba si la lista está vacía.
    • Si está vacío, hacemos que el subproceso del consumidor abandone el bloqueo en la PC y pasa el control al subproceso del productor para producir más trabajos.
    • Si la lista no está vacía, damos la vuelta al bucle y eliminamos un elemento de la lista.
  • En ambos métodos, usamos notificar al final de todas las declaraciones. La razón es simple, una vez que tiene algo en la lista, puede hacer que el subproceso del consumidor lo consuma, o si ha consumido algo, puede hacer que el productor produzca algo.
  • sleep() al final de ambos métodos simplemente haga que la salida del programa se ejecute paso a paso y no muestre todo a la vez para que pueda ver lo que realmente está sucediendo en el programa.

Ejercicio :  

  • Se recomienda a los lectores que utilicen la condición if en lugar del bucle interno para comprobar las condiciones de contorno.
  • Intente hacer que su programa produzca un artículo e inmediatamente después haga que el consumidor lo consuma antes de que el productor produzca cualquier otro artículo.

Referencia: https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem

Este artículo es una contribución de Rishabh Mahrsee . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *