Comprender los hilos en el problema del consumidor del productor | Java

Thread es una parte de la ejecución, es decir, es una ruta de ejecución independiente en un programa. Un programa puede tener más de un subproceso, lo que plantea el concepto de subprocesos múltiples . Debemos usar la clase java.lang.Thread para usar un hilo para realizar una tarea específica. En este artículo, veamos la implementación de los hilos a través de un programa. 
Cada vez que se ejecuta un programa, la JVM primero verifica la existencia del método «Principal». Si el método está presente, por defecto crea un subproceso y este subproceso se llama «subproceso principal» porque es responsable de la ejecución de las declaraciones que están presentes en el método principal. Un subproceso puede estar en varios estados que se analizan en este artículo
Hay dos formas de crear un hilo. Están: 
 

  1. Creando un objeto para la clase Thread.
  2. Mediante el uso de la interfaz ejecutable.

Creación de subprocesos mediante la ampliación de la clase Subproceso: creamos una clase que amplía la clase java.lang.Thread. Esta clase anula el método run() disponible en la clase Thread. Un hilo comienza su vida dentro del método run() . Creamos un objeto de la clase hilo y llamamos al método start() para iniciar la ejecución de un hilo. Start() invoca el método run() en el objeto del hilo. Veamos un ejemplo para encontrar el factorial usando el hilo:
 

Java

// Java program to find the factorial
// of a number by the implementation
// of threads using thread class
 
class ThreadImplement extends Thread {
    static int fact = 1;
 
    // Overriding the run method
    // to find the factorial of
    // a number 5
    public void run()
    {
 
        // Loop to compute the factorial
        for (int i = 1; i <= 5; i++)
            fact = fact * i;
        System.out.println(fact);
    }
}
 
// Class to create a thread and
// compute the factorial
public class GFG {
    public static void main(String[] args)
    {
        // Creating an object of the
        // thread class
        Thread t1
            = new Thread(new ThreadImplement());
 
        // Computing the above class
        t1.start();
    }
}
Producción: 

120

 

Creación de subprocesos mediante la implementación de la interfaz ejecutable: la clase de subproceso implementa la interfaz ejecutable y la interfaz ejecutable contiene solo el método run() . Hay cerca de 37 métodos presentes en la clase de subprocesos, pero comúnmente usamos 22. Todas las tareas que deben ser ejecutadas por el subproceso deben colocarse dentro del método run(), es decir, debemos anular el método run(). Para iniciar un hilo, debemos usar el método start() . Después de iniciar un hilo, este hilo ejecutará las declaraciones que están presentes en el método run() . Implementemos el mismo programa factorial usando la interfaz ejecutable:
 

Java

// Java program to find the factorial
// of a number by the implementation
// of threads using runnable interface
 
class ThreadImplement implements Runnable {
    static int fact = 1;
 
    // Overriding the run method
    // to find the factorial of
    // a number 5
    public void run()
    {
        // Loop to compute the factorial
        for (int i = 1; i <= 5; i++)
            fact = fact * i;
        System.out.println(fact);
    }
}
 
// Class to create a thread and
// compute the factorial
public class GFG {
    public static void main(String[] args)
    {
 
        // Creating an object of the
        // thread class
        Thread t1
            = new Thread(new ThreadImplement());
 
        // Computing the above class
        t1.start();
    }
}
Producción: 

120

 

Subprocesos múltiples en Java: en informática, el problema 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.

En este problema, necesitamos dos subprocesos, Subproceso t1 (produce los datos) y Subproceso t2 (consume los datos). Sin embargo, ambos subprocesos no deberían ejecutarse simultáneamente. 
 

A continuación se muestra la implementación del problema productor-consumidor:
 

Java

// Java program to implement the
// producer consumer's problem
 
import java.lang.Thread;
 
// Producer class which extends the
// thread
class Producer extends Thread {
 
    // Creating a string buffer
    StringBuffer buffer;
    boolean dp = false;
 
    // Initializing the string buffer
    Producer()
    {
        buffer = new StringBuffer(4);
    }
 
    // Overriding the run method
    public void run()
    {
        synchronized (buffer)
        {
 
            // Adding the data into the
            // buffer
            for (int i = 0; i < 4; i++) {
                try {
                    buffer.append(i);
                    System.out.println("Produced " + i);
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
 
            // Notifying the buffer
            System.out.println("Buffer is FUll");
            buffer.notify();
        }
    }
}
 
// Consumer class which extends
// the thread
class Consumer extends Thread {
 
    // Creating the object of the
    // producer class
    Producer p;
 
    // Assigning the object of the
    // producer class
    Consumer(Producer temp)
    {
        p = temp;
    }
 
    // Overriding the run method
    public void run()
    {
 
        // Controlling the access of the
        // buffer to the shared producer
        synchronized (p.buffer)
        {
            try {
                p.buffer.wait();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
 
            // Printing the values of the string buffer
            // and consuming the buffer
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
            System.out.println("\nBuffer is Empty");
        }
    }
}
 
// Main class to implement the
// Producer Consumer problem
class GFG {
    public static void main(String args[])
    {
        // Initially, there needs to be some data
        // in order to consume the data. So,
        // Producer object is created first
        Producer p = new Producer();
 
        // Sending this producer object
        // into the consumer
        Consumer c = new Consumer(p);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
 
        // Since from the producer object,
        // we have already produced the data.
        // So, we start by consuming it.
        t2.start();
        t1.start();
    }
}
Producción: 

Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty

 

Problema del consumidor del productor sin sincronización: el código anterior es ineficiente porque los recursos de la CPU no se utilizan de manera eficiente. Los subprocesos están esperando el búfer en el estado de espera. En lugar de esto, podemos utilizar estos subprocesos de manera eficiente finalizándolos y volviéndolos a crear. Eso es: 
 

  • Creamos un hilo para producir los datos.
  • Una vez que el búfer esté lleno, terminaremos ese hilo.
  • Cree otro subproceso para consumir los datos (en este punto, el subproceso productor está muerto).
  • Una vez que el búfer está vacío, el subproceso del consumidor termina y el subproceso del productor se crea y produce los datos (en este punto, el subproceso del consumidor está muerto).

A continuación se muestra la implementación del enfoque anterior:
 

Java

// Java program to implement the
// producer consumer's problem
// without using synchronization
 
import java.lang.Thread;
 
// Producer class which extends the
// thread
class Producer extends Thread {
 
    // Creating a string buffer
    StringBuffer buffer;
 
    // Initializing the string buffer
    Producer()
    {
        buffer = new StringBuffer(4);
    }
 
    // Overriding the run method
    public void run()
    {
 
        // Loop to add data into the
        // buffer
        for (int i = 0; i < 4; i++) {
            try {
                buffer.append(i);
                System.out.println("Produced " + i);
            }
            catch (Exception e) {
 
                // Exception is returned when
                // the buffer is not accessible
                e.printStackTrace();
            }
        }
        System.out.println("Buffer is FUll");
 
        // Creating a consumer object after
        // execution of the above method.
        // Here, this keyword refers to
        // the current object of the
        // producer. This object is passed
        // into the consumer to access the
        // created buffer
        Consumer c = new Consumer(this);
    }
}
 
// Consumer class which extends
// the thread
class Consumer extends Thread {
    Producer p;
    Thread t2;
 
    // Constructor to get the
    // producer object
    Consumer(Producer temp)
    {
        p = temp;
 
        // Creating a new thread for
        // the object
        t2 = new Thread(this);
        t2.start();
    }
 
    // Overriding the run method
    public void run()
    {
        try {
 
            // Printing the string buffer and
            // consuming it
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\nBuffer is Empty");
    }
}
 
// Main class to implement the
// Producer Consumer problem
class Efficiency {
    public static void main(String args[])
    {
        // Producer object is created and
        // passed into the thread.
        Producer p = new Producer();
        Thread t1 = new Thread(p);
 
        // Here, instead of the same
        // thread waiting, a new thread
        // is created in the constructor
        // of the consumer class
        t1.start();
    }
}
Producción: 

Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty

 

Problema del consumidor del productor modificado: el enfoque anterior se puede mejorar aún más porque el productor y el consumidor utilizan el mismo búfer. Entonces, en lugar de usar varios subprocesos, use un subproceso de modo que inicialmente, el búfer esté vacío y el subproceso que se creó actúe como productor. Una vez que el búfer está lleno, este subproceso actúa como consumidor y consume los datos. Sin embargo, tenemos que evitar el punto muerto. 
 

A continuación se muestra la implementación del enfoque anterior:
 

Java

// Java program to implement the
// producer consumer's problem
// using single thread
import java.lang.Thread;
 
// Producer class which extends the
// thread
class Producer extends Thread {
 
    // Creating a string buffer
    StringBuffer buffer;
 
    // Variable to avoid the deadlock
    boolean dp = false;
    Thread t1;
    Producer()
    {
        // Initializing the buffer
        buffer = new StringBuffer(4);
 
        // Creating a new thread with
        // the current object
        t1 = new Thread(this);
        t1.start();
    }
 
    // Overriding the run method
    public void run()
    {
 
        // Loop to produce the
        // data and add it to
        // the buffer
        for (int i = 0; i < 4; i++) {
            try {
                buffer.append(i);
                System.out.println("Produced " + i);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("Buffer is FUll");
 
        // Creating a consumer object
        // by passing the current
        // producer object
        Consumer c = new Consumer(this);
 
        // Reinitializing the thread
        // with the new value of the
        // consumer object
        t1 = new Thread(c);
        t1.start();
    }
}
 
// Consumer class which extends
// the thread
class Consumer extends Thread {
    Producer p;
 
    // Constructor to initialize
    // with the producer object
    Consumer(Producer temp)
    {
        p = temp;
    }
 
    // Overriding the run method
    public void run()
    {
        try {
 
            // Loop to print the buffer and
            // consume the values
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\nBuffer is Empty");
    }
}
 
// Main class to implement the
// Producer Consumer problem
class GFG {
    public static void main(String args[])
    {
 
        // Creating the object of the
        // producer
        Producer p = new Producer();
    }
}
Producción: 

Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty

 

Publicación traducida automáticamente

Artículo escrito por sahithchoudary123 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 *