Tutorial de subprocesamiento múltiple de Java

Los subprocesos son la columna vertebral de los subprocesos múltiples. Vivimos en un mundo real que en sí mismo está atrapado en la web rodeado de muchas aplicaciones. Lo mismo con el avance de las tecnologías que no podemos compensar con la velocidad por la que necesitamos ejecutarlas simultáneamente por lo que necesitamos más aplicaciones para ejecutar en paralelo. Se logra mediante el concepto de hilo. 

Java-Multithreading-Tutorial

Ejemplo de la vida real 

Suponga que está usando dos tareas a la vez en la computadora, ya sea usando Microsoft Word y escuchando música. Estas dos tareas se denominan procesos . Así que empiezas a escribir en Word y al mismo tiempo música arriba, esto se llama multitarea . Ahora cometió un error en Word y el corrector ortográfico muestra una excepción, esto significa que incluso Word es un proceso que se divide en subprocesos. Ahora, si una máquina es de doble núcleo, entonces un proceso o tarea es manejado por un núcleo y la música es manejada por otro núcleo. 

En el ejemplo anterior, nos encontramos con que tanto el multiprocesamiento como el multihilo se usan indirectamente para lograr la multitarea. Hemos logrado De esta manera el mecanismo de dividir las tareas se llama multithreading en el que cada proceso o tarea es llamado por un hilo donde un hilo es responsable de cuándo ejecutar, cuándo parar y cuánto tiempo estar en estado de espera. Por lo tanto, un subproceso es la unidad de procesamiento más pequeña, mientras que la multitarea es un proceso de ejecución de varias tareas a la vez.

La multitarea se está logrando de dos maneras :

  1. Multiprocesamiento : la multitarea basada en procesos es un proceso pesado y ocupa diferentes espacios de direcciones en la memoria. Por lo tanto, si bien el cambio de un proceso a otro requerirá algo de tiempo, aunque sea muy pequeño, lo que provocará un retraso durante el cambio, ya que los registros se cargarán en los mapas de memoria y la lista se actualizará.
  2. Multiproceso : la multitarea basada en subprocesos es un proceso ligero y ocupa el mismo espacio de direcciones. Por lo tanto, mientras que cambiar el costo de la comunicación será muy inferior.

A continuación se ilustra el ciclo de vida de un subproceso

  1. Nuevo : cuando se acaba de crear un hilo.
  2. Ejecutable : cuando se llama a un método start() sobre un subproceso procesado por el programador de subprocesos.
    • Caso A: puede ser un subproceso en ejecución
    • Caso B: no puede ser un subproceso en ejecución
  3. En ejecución : cuando llega al caso 1, significa que el programador lo ha seleccionado para ejecutar el subproceso desde el estado ejecutable al estado de ejecución.
  4. Bloqueado : cuando llega al caso 2, lo que significa que el planificador ha seleccionado no permitir que un subproceso cambie el estado de ejecutable a ejecutar.
  5. Terminado : cuando existe el método run() o se llama al método stop() a través de un subproceso.

Si incorporamos subprocesos en los sistemas operativos, se puede percibir que los algoritmos de programación de procesos en los sistemas operativos están trabajando en profundidad en el mismo concepto que incorpora el subproceso en los diagramas de Gantt . A continuación se enumeran algunos de los más populares, que los resumen a todos y se utilizan prácticamente en el desarrollo de software.

  • Primero en entrar primero en salir
  • Último en entrar primero en salir
  • Programación por turnos

Ahora uno Imagine el concepto de Deadlock en sistemas operativos con subprocesos ahora cómo se calcula internamente el cambio si solo tiene una descripción general de ellos. 

Hasta ahora, hemos entendido los subprocesos múltiples y los subprocesos conceptualmente, por lo que podemos concluir las ventajas de los subprocesos múltiples antes de introducir cualquier otro concepto o llegar a los programas en subprocesos múltiples.

  • El usuario no está bloqueado ya que los subprocesos son independientes, incluso si hay un problema con un subproceso, solo se detendrá el proceso correspondiente, el resto de las operaciones se calcularán correctamente.
  • Ahorra tiempo, ya que se llevan a cabo demasiadas operaciones al mismo tiempo, lo que hace que el trabajo se termine, ya que si no se utilizan subprocesos, el procesador manejará el único proceso.
  • Los subprocesos son independientes aunque comparten el mismo espacio de direcciones.

Así que hemos tocado todos los conceptos principales de subprocesos múltiples, pero queda la pregunta que se esfuerza en la cabeza. ¿Por qué lo necesitamos, dónde usarlo y cómo? Ahora, discutiremos los tres escenarios por los que se necesitan subprocesos múltiples y dónde se implementa a través de la ayuda de programas en los que aprenderemos más sobre los subprocesos y sus métodos. Necesitamos subprocesos múltiples en los cuatro escenarios enumerados.

  • Clase de hilo
  • Aplicaciones móviles
    • Subproceso asíncrono
  • aplicaciones web
  • Desarrollo de juegos

Nota: De forma predeterminada, solo tenemos un subproceso principal que es responsable de la excepción del subproceso principal que ha encontrado, incluso sin tener ningún conocimiento previo de subprocesos múltiples.

Dos formas de implementar subprocesos múltiples

Método 1: usar la clase Thread

Java proporciona la clase Thread para lograr la programación invocando hilos, por lo que a continuación se muestran algunos métodos principales de la clase thread en el formato tabular con el que tratamos con frecuencia a lo largo de la acción realizada por ellos.

Métodos  Acción realizada
esDaemon() Comprueba si el hilo actual es daemon o no.
comienzo() Inicia la ejecución del hilo.
correr() Hace las declaraciones de operaciones ejecutables en el cuerpo de este método sobre un hilo
dormir() Es un método estático que pone el hilo a dormir durante un cierto tiempo que se le pasa como argumento.
Espere() Vuelve a poner el hilo en estado de espera.
notificar() Da una notificación a un hilo que está en estado de espera. 
notificar a todos() Da una notificación a todo el hilo en el estado de espera 
establecerDemonio() Estableció el hilo actual como hilo Daemon
deténgase() Se utiliza para detener la ejecución del hilo.
reanudar() Se utiliza para reanudar el hilo suspendido.

Requisitos previos: sintaxis básica y métodos para tratar con hilos

Ahora veamos cómo configurar el nombre del hilo. De forma predeterminada, los subprocesos se denominan subproceso-0, subproceso-1, etc. Pero también hay un método que se usa a menudo como método setName() . También correspondiente a él hay un método getName() que devuelve el nombre del subproceso, ya sea predeterminado o ya resuelto mediante el uso del método setName() . La sintaxis es la siguiente:

Sintaxis: 

(a) Devolver el nombre del hilo

public String getName() ;

(b) Cambiar el nombre del hilo

 public void setName(String name);

Dando un paso más allá, profundicemos en la parte de implementación para adquirir más conceptos sobre subprocesos múltiples. Entonces, hay básicamente dos formas de implementar subprocesos múltiples:

Ilustración: Considere si uno tiene que multiplicar todos los elementos por 2 y hay 500 elementos en una array.

Ejemplos  

Java

// Case 1
// Java Program to illustrate Creation and execution of
// thread via start() and run() method in Single inheritance
 
// Class 1
// Helper thread Class extending main Thread Class
class MyThread1 extends Thread {
 
    // Method inside MyThread2
    // run() method which is called as
    // soon as thread is started
    public void run()
    {
 
        // Print statement when the thread is called
        System.out.println("Thread1 is running");
    }
}
 
// Class 2
// Main thread Class extending main Thread Class
class MyThread2 extends Thread {
 
    // Method inside MyThread2
    // run() method which is called
    // as soon as thread is started
    public void run()
    {
 
        // run() method which is called as soon as thread is
        // started
 
        // Print statement when the thread is called
        System.out.println("Thread2 is running");
    }
}
 
// Class 3
// Main Class
class GFG {
 
    // Main method
    public static void main(String[] args)
    {
 
        // Creating a thread object of our thread class
        MyThread1 obj1 = new MyThread1();
        MyThread2 obj2 = new MyThread2();
 
        // Getting the threads to the run state
 
        // This thread will transcend from runnable to run
        // as start() method will look for run() and execute
        // it
        obj1.start();
 
        // This thread will also transcend from runnable to
        // run as start() method will look for run() and
        // execute it
        obj2.start();
    }
}

Java

// Case 2
// Java Program to illustrate Difference between Runnable
// & Non-runnable Threads And Single Inheritance
 
// Class 1
// Helper thread Class extending main Thread Class
class MyThread1 extends Thread {
 
    // Method inside MyThread2
    // run() method which is called as soon as thread is
    // started
    public void run() {
 
        // Print statement when the thread is called
        System.out.println("Thread 1 is running");
    }
}
 
// Class 2
// Main thread Class extending main Thread Class
class MyThread2 extends Thread {
 
    // Method
    public void show() {
 
        // Print statement when thread is called
        System.out.println("Thread 2");
    }
}
 
// Class 3
// Main Class
class GFG {
 
    // Main method
    public static void main(String[] args) {
 
        // Creating a thread object of our thread class
        MyThread1 obj1 = new MyThread1();
        MyThread2 obj2 = new MyThread2();
 
        // Getting the threads to the run state
 
        // This thread will transcend from runnable to run
        // as start() method will look for run() and execute
        // it
        obj1.start();
 
        // This thread will now look for run() method which is absent
        // Thread is simply created not runnable
        obj2.start();
    }
}

Java

// Java Program to illustrate difference between
// start() method thread vs show() method
 
// Class 1
// Helper thread Class extending main Thread Class
class MyThread1 extends Thread {
 
    // Method inside MyThread2
    // run() method which is called as soon as thread is
    // started
    public void run() {
 
        // Print statement when the thread is called
        System.out.println("Thread 1 is running");
    }
}
 
// Class 2
// Main thread Class extending main Thread Class
class MyThread2 extends Thread {
 
    // Method
    public void show() {
 
        // Print statement when thread is called
        System.out.println("Thread 2");
    }
}
 
// Class 3
// Main Class
class GFG {
 
    // Main method
    public static void main(String[] args) {
 
        // Creating a thread object of our thread class
        MyThread1 obj1 = new MyThread1();
        MyThread2 obj2 = new MyThread2();
 
        // Getting the threads to the run state
 
        // This thread will transcend from runnable to run
        // as start() method will look for run() and execute
        // it
        obj1.start();
 
        // This thread is simply a function call as
        // no start() method is executed so here only
        // thread is created only followed by call
        obj2.show();
    }
}

Producción:

Caso 1: 

Thread1 is running
Thread2 is running

Aquí hemos creado nuestras dos clases de subprocesos para cada subproceso. En el método principal , simplemente estamos creando objetos de estas clases de subprocesos donde los objetos ahora son subprocesos. Entonces, en main , llamamos al hilo usando el método start() sobre ambos hilos. Ahora el método start() inicia el hilo y busca su método run() para ejecutar. Aquí, nuestras dos clases de subprocesos tenían métodos run(), por lo que el planificador pone ambos subprocesos en estado de ejecución desde ejecutable, y la salida en la consola está justificada.

Caso 2:

Thread 1 is running

Aquí hemos creado nuestras dos clases de subprocesos para cada subproceso. En el método principal, simplemente estamos creando objetos de estas clases de subprocesos donde los objetos ahora son subprocesos. Entonces, en main, llamamos al subproceso usando el método start() sobre ambos subprocesos. Ahora el método start() inicia el hilo y busca su método run() para ejecutar. Aquí solo la clase 1 tiene el método run() para hacer que el subproceso trascienda del estado ejecutable al tun para ejecutarse, mientras que el subproceso 2 solo se crea pero el planificador no lo pone en estado de ejecución ya que faltaba su método run() correspondiente. Por lo tanto, solo el subproceso 1 se llama rest subproceso 2 se crea únicamente y está en el estado ejecutable más tarde bloqueado por el programador porque faltaba el método run() correspondiente.

Caso 3:

Thread 2
Thread 1 is running

Método 2: Uso de la interfaz ejecutable 

Otra forma de lograr subprocesos múltiples en Java es a través de la interfaz Runnable. Aquí, como hemos visto en el ejemplo anterior en la forma 1, donde se extiende la clase Thread. Aquí, la interfaz Runnable, que es una interfaz funcional, tiene su propio método run(). Aquí las clases se implementan en la interfaz Runnable. Más adelante, en el método main(), se crea una referencia Runnable para las clases que se implementan con el fin de vincularse con la clase Thread para ejecutar nuestros propios métodos run() correspondientes. Además, al crear un objeto de la clase Thread, pasaremos estas referencias en la clase Thread, ya que su constructor permite un único objeto ejecutable, que se pasa como parámetro al crear el objeto de la clase Thread en un método main(). Ahora, por último, probablemente lo que hicimos en la clase Thread, El método start() se invoca sobre el objeto ejecutable que ahora ya está vinculado con los objetos de la clase Thread, por lo que la ejecución comienza para nuestros métodos run() en el caso de la interfaz Runnable. Se muestra en el siguiente programa de la siguiente manera:

Ejemplo:

Java

// Java Program to illustrate Runnable Interface in threads
// as multiple inheritance is not allowed
 
// Importing basic packages
import java.io.*;
import java.util.*;
 
// Class 1
// Helper class implementing Runnable interface
class MyThread1 implements Runnable {
 
    // run() method inside this class
    public void run()
    {
        // Iterating to get more execution of threads
        for (int i = 0; i < 5; i++) {
 
            // Print statement whenever run() method
            // of this class is called
            System.out.println("Thread1");
 
            // Getting sleep method in try block to
            // check for any exceptions
            try {
                // Making the thread pause for a certain
                // time using sleep() method
                Thread.sleep(1000);
            }
 
            // Catch block to handle the exceptions
            catch (Exception e) {
            }
        }
    }
}
 
// Class 2
// Helper class implementing Runnable interface
class MyThread2 implements Runnable {
 
    // run() method inside this class
    public void run()
    {
        for (int i = 0; i < 5; i++) {
 
            // Print statement whenever run() method
            // of this class is called
            System.out.println("Thread2");
 
            // Getting sleep method in try block to
            // check for any exceptions
            try {
 
                // Making the thread pause for a certain
                // time
                // using sleep() method
                Thread.sleep(1000);
            }
 
            // Catch block to handle the exceptions
            catch (Exception e) {
            }
        }
    }
}
 
// Class 3
// Main class
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating reference of Runnable to
        // our classes above in main() method
        Runnable obj1 = new MyThread1();
        Runnable obj2 = new MyThread2();
 
        // Creating reference of thread class
        // by passing object of Runnable in constructor of
        // Thread class
        Thread t1 = new Thread(obj1);
        Thread t2 = new Thread(obj2);
 
        // Starting the execution of our own run() method
        // in the classes above
        t1.start();
        t2.start();
    }
}
Producción

Thread2
Thread1
Thread2
Thread1
Thread2
Thread1
Thread2
Thread1
Thread2
Thread1

Puntos para recordar: siempre que desee crear hilos, solo hay dos formas:

  1. Extendiendo la clase
  2. Implementando la interfaz que es ejecutable

Asegúrese de crear un objeto de hilos en el que tenga que pasar el objeto de ejecutable  

Métodos especiales de hilos
 

Ahora analicemos que existen varios métodos para los hilos. Aquí discutiremos los principales para tener una comprensión práctica de los subprocesos y subprocesos múltiples, que son secuenciales de la siguiente manera: 

  1. método de inicio()
  2. método suspender() 
  3. método de parada() 
  4. método esperar() 
  5. método de notificación() 
  6. Método de notificación a todos()
  7. método dormir()
    • Salida sin método sleep()
    • Salida con el método sleep() en procesos de ejecución en serie (enfoque de métodos de bloqueo)
    • Salida con el método sleep() en procesos de ejecución en paralelo (enfoque de métodos de desbloqueo)
  8. método join()

Nota: Para los usuarios inexpertos en subprocesos múltiples donde los subprocesos son la columna vertebral, vaya al Programa 4 para obtener los conceptos básicos de los subprocesos, cómo iniciar, hacer que se mantenga o terminar, luego solo cambie al programa 1 y descanse de la siguiente manera. 

Implementación: 

Java

// Example 1
// Java Program to illustrate Output Without sleep() Method
 
// Class 1
// Helper Class 1
class Shot extends Thread {
 
    // Method 1
    public void show() {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement whenever method
            // of this class is called
            System.out.println("Shot");
 
        }
    }
}
 
// Class 2
// Helper Class 2
class Miss extends Thread {
 
    // Method 2
    public void show() {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement whenever method
            // of this class is called
            System.out.println("Miss");
 
        }
    }
 
}
 
// Class 3
// Main class
public class GFG {
 
    // Method 3
    // Main method
    public static void main(String[] args) {
 
        // Creating objects in the main() method
        // of class 1 and class 2
        Shot obj1 = new Shot();
        Miss obj2 = new Miss();
 
        // Calling methods of the class 1 and class 2
        obj1.show();
        obj2.show();
 
    }
}

Java

// Example 2
// Java Program to illustrate Output Using sleep() Method
// in Serial Execution
 
// Class 1
// Helper Class 1
class Shot extends Thread {
 
    // Method 1
    // public void show() {
    public void show()
    {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement
            System.out.println("Shot");
 
            // Making thread to sleep using sleep() method
 
            // Try-catch block for exceptions
            try {
                Thread.sleep(1000);
            }
            catch (Exception e) {
            }
        }
    }
}
 
// Class 2
// Helper Class 2 Hello
class Miss extends Thread {
 
    // Method 2
    // public void show() {
    public void show()
    {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement
            System.out.println("Miss");
 
            // Making thread to sleep using sleep() method
 
            // Try-catch block for exceptions
            try {
                Thread.sleep(1000);
            }
            catch (Exception e) {
            }
        }
    }
}
 
// Class 3
// Main class
public class GFG {
 
    // Method 3
    // Main method
    public static void main(String[] args)
    {
 
        // Creating objects in the main() method
        Shot obj1 = new Shot();
        Miss obj2 = new Miss();
 
        // Starting the thread objects
        obj1.start();
        obj2.start();
 
        // Calling methods of class 1 and class 2
        obj1.show();
        obj2.show();
    }
}

Java

// Example 3
// Java Program to illustrate Output Using sleep() Method
// in Parallel Execution
 
// Class 1
// Helper Class 1
class Shot extends Thread {
 
    // Method 1
    // public void show() {
    public void run()
    {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement
            System.out.println("Shot");
 
            // Making thread to sleep using sleep() method
 
            // Try catch block for exceptions
            try {
                Thread.sleep(1000);
            }
            catch (Exception e) {
            }
        }
    }
}
 
// Class 2
// Helper Class 2 Hello
class Miss extends Thread {
 
    // Method 2
    // public void show() {
    public void run()
    {
 
        // Iterating to print more number of times
        for (int i = 0; i < 5; i++) {
 
            // Print statement
            System.out.println("Miss");
 
            // Making thread to sleep using sleep() method
 
            // Try catch block for exceptions
            try {
                Thread.sleep(1000);
            }
            catch (Exception e) {
            }
        }
    }
}
 
// Class 3
// Main class
public class GFG {
 
    // Method 3
    // Main method
    public static void main(String[] args)
    {
 
        // Creating objects in the main() method
        Shot obj1 = new Shot();
        Miss obj2 = new Miss();
 
        // Starting the thread objects
        // using start() method
 
        // start() method calls the run() method
        // automatically
        obj1.start();
        obj2.start();
    }
}

Producción:

Caso 1:

Shot
Shot
Shot
Shot
Shot
Miss
Miss
Miss
Miss
Miss

Caso 2: Salida de vídeo

Shot
Shot
Shot
Shot
Shot
Miss
Miss
Miss
Miss
Miss

Caso 3: Salida de video 

Shot
Miss
Shot
Miss
Shot
Miss
Shot
Miss
Shot
Miss

Nota: No se ha establecido una prioridad para los subprocesos para los cuales, según el orden de ejecución de los subprocesos, las salidas variarán, así que recuerde este inconveniente de subprocesos múltiples de diferentes salidas que conducen a problemas de inconsistencia de datos que discutiremos en profundidad en la parte posterior. bajo sincronización en hilos. 

Prioridades en hilos

Prioridades en subprocesos es un concepto en el que cada subproceso tiene una prioridad que, en lenguaje sencillo, se puede decir que cada objeto tiene prioridad aquí, que se representa mediante números que van del 1 al 10. 

  • La prioridad predeterminada se establece en 5 como excepción.
  • La prioridad mínima se establece en 0.
  • La prioridad máxima se establece en 10.

Aquí se definen 3 constantes, a saber, de la siguiente manera:

  1. public static int NORM_PRIORITY
  2. public static int MIN_PRIORITY
  3. public static int MAX_PRIORITY

Discutámoslo con un ejemplo para obtener cómo se ejecuta internamente el trabajo. Aquí usaremos el conocimiento recopilado anteriormente de la siguiente manera:

Java

// Java Program to illustrate Priority Threads
// Case 1: No priority is assigned (Default priority)
 
// Importing input output thread class
import java.io.*;
// Importing Thread class from java.util package
import java.util.*;
 
// Class 1
// Helper Class (Our thread  class)
class MyThread extends Thread {
 
    public void run()
    {
 
        // Printing the current running thread via getName()
        // method using currentThread() method
        System.out.println("Running Thread : "
                           + currentThread().getName());
 
        // Print and display the priority of current thread
        // via currentThread() using getPriority() method
        System.out.println("Running Thread Priority : "
                           + currentThread().getPriority());
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating objects of MyThread(above class)
        // in the main() method
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
 
        // Case 1: Default Priority no setting
        t1.start();
        t2.start();
    }
}

Java

// Java Program to illustrate Priority Threads
// Case 2: NORM_PRIORITY
 
// Importing input output thread class
import java.io.*;
// Importing Thread class from java.util package
import java.util.*;
 
// Class 1
// Helper Class (Our thread  class)
class MyThread extends Thread {
 
    // run() method to transit thread from
    // runnable to run state
    public void run()
    {
 
        // Printing the current running thread via getName()
        // method using currentThread() method
        System.out.println("Running Thread : "
                           + currentThread().getName());
 
        // Print and display the priority of current thread
        // via currentThread() using getPriority() method
        System.out.println("Running Thread Priority : "
                           + currentThread().getPriority());
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating objects of MyThread(above class)
        // in the main() method
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
 
        // Setting priority to thread via NORM_PRIORITY
        // which set priority to 5 as default thread
        t1.setPriority(Thread.NORM_PRIORITY);
        t2.setPriority(Thread.NORM_PRIORITY);
 
        // Setting default priority using
        // NORM_PRIORITY
        t1.start();
        t2.start();
    }
}

Java

// Java Program to illustrate Priority Threads
// Case 3: MIN_PRIORITY
 
// Importing input output thread class
import java.io.*;
// Importing Thread class from java.util package
import java.util.*;
 
// Class 1
// Helper Class (Our thread  class)
class MyThread extends Thread {
 
    // run() method to transit thread from
    // runnable to run state
    public void run()
    {
 
        // Printing the current running thread via getName()
        // method using currentThread() method
        System.out.println("Running Thread : "
                           + currentThread().getName());
 
        // Print and display the priority of current thread
        // via currentThread() using getPriority() method
        System.out.println("Running Thread Priority : "
                           + currentThread().getPriority());
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating objects of MyThread(above class)
        // in the main() method
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
 
        // Setting priority to thread via NORM_PRIORITY
        // which set priority to 1 as least priority thread
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);
 
        // Setting default priority using
        // NORM_PRIORITY
        t1.start();
        t2.start();
    }
}

Java

// Java Program to illustrate Priority Threads
// Case 4: MAX_PRIORITY
 
// Importing input output thread class
import java.io.*;
// Importing Thread class from java.util package
import java.util.*;
 
// Class 1
// Helper Class (Our thread  class)
class MyThread extends Thread {
 
    // run() method to transit thread from
    // runnable to run state
    public void run()
    {
 
        // Printing the current running thread via getName()
        // method using currentThread() method
        System.out.println("Running Thread : "
                           + currentThread().getName());
 
        // Print and display the priority of current thread
        // via currentThread() using getPriority() method
        System.out.println("Running Thread Priority : "
                           + currentThread().getPriority());
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating objects of MyThread(above class)
        // in the main() method
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
 
        // Setting priority to thread via MAX_PRIORITY
        // which set priority to 1 as most priority thread
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
 
        // Setting default priority using
        // MAX_PRIORITY
 
        // Starting the threads using start() method
        // which automatically invokes run() method
        t1.start();
        t2.start();
    }
}

Producción:

Caso 1: Prioridad predeterminada

Running Thread : Thread-0
Running Thread : Thread-1
Running Thread Priority : 5
Running Thread Priority : 5

Caso 2: NORM_PRIORIDAD

Running Thread : Thread-0
Running Thread : Thread-1
Running Thread Priority : 5
Running Thread Priority : 5

Caso 3: MIN_PRIORIDAD

Running Thread : Thread-0
Running Thread : Thread-1
Running Thread Priority : 1
Running Thread Priority : 1

Caso 4: MAX_PRIORIDAD

Running Thread : Thread-1
Running Thread : Thread-0
Running Thread Priority : 10
Running Thread Priority : 10

Explicación de salida

Si observamos con cuidado, vemos que las salidas para los casos 1 y 2 son equivalentes. Esto significa que cuando el usuario ni siquiera es consciente de los subprocesos prioritarios, NORM_PRIORITY muestra el mismo resultado hasta la prioridad predeterminada. Esto se debe a que la prioridad predeterminada de ejecutar el subproceso tan pronto como se llama al método start() correspondiente se ejecuta según la configuración de prioridades para todo el subproceso en 5, lo que equivale a la prioridad del caso NORM. Esto se debe a que ambas salidas son equivalentes entre sí. Mientras que en el caso 3 la prioridad se establece en un mínimo en una escala de 1 a 10, haga lo mismo en el caso 4 donde la prioridad se asigna a 10 en la misma escala. 

Por lo tanto, todos los productos en términos de prioridades están justificados. Ahora avancemos hacia un aspecto importante del subprocesamiento prioritario que se ha incorporado en la vida diaria: el subproceso Daemon.

El subproceso Daemon es básicamente un subproceso de proveedor de servicios que proporciona servicios al subproceso de usuario. El alcance de este inicio de subproceso() o terminar() depende completamente del subproceso del usuario, ya que admite en el backend para que se ejecuten los subprocesos del usuario. Tan pronto como finaliza el subproceso del usuario, el subproceso daemon también finaliza al mismo tiempo que el subproceso del proveedor de servicios.

Por lo tanto, las características del subproceso Daemon son las siguientes:

  • Solo el subproceso del proveedor de servicios no es responsable de la interpretación en los subprocesos de los usuarios.
  • Por lo tanto, es un hilo de baja prioridad.
  • Es un subproceso dependiente ya que no tiene existencia por sí mismo.
  • JVM finaliza el subproceso tan pronto como finalizan los subprocesos del usuario y vuelve a funcionar cuando comienza el subproceso del usuario.
  • Sí, supongo que el ejemplo más popular es el recolector de basura en Java. Algunos otros ejemplos incluyen ‘finalizador’.

Excepciones: IllegalArgumentException como tipo de retorno al establecer un subproceso Daemon es booleano, así que aplique con cuidado.

Nota: Para deshacerse de la excepción, el subproceso de los usuarios solo debe comenzar después de configurarlo como subproceso daemon. La otra forma de comenzar antes de configurarlo como daemon no funcionará, ya que aparecerá IllegalArgumentException 

Como se discutió anteriormente en la clase Thread dos, el método más utilizado es el siguiente:

Métodos de subprocesos de daemon  Acción realizada 
esDaemon() Comprueba si el subproceso actual es un subproceso adaemon o no 
establecerDemonio() Configuró el hilo para que se marcara como hilo daemon.

Analicemos la implementación del subproceso Daemon antes de saltar al recolector de basura.

Java

// Java Program to show Working of Daemon Thread
// with users threads
 
import java.io.*;
// Importing Thread class from java.util package
import java.util.*;
 
// Class 1
// Helper Class extending Thread class
class CheckingMyDaemonThread extends Thread {
 
    // Method
    // run() method which is invoked as soon as
    // thread start via start()
    public void run()
    {
 
        // Checking whether the thread is daemon thread or
        // not
        if (Thread.currentThread().isDaemon()) {
 
            // Print statement when Daemon thread is called
            System.out.println(
                "I am daemon thread and I am working");
        }
 
        else {
 
            // Print statement whenever users thread is
            // called
            System.out.println(
                "I am user thread and I am working");
        }
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating threads in the main body
        CheckingMyDaemonThread t1
            = new CheckingMyDaemonThread();
        CheckingMyDaemonThread t2
            = new CheckingMyDaemonThread();
        CheckingMyDaemonThread t3
            = new CheckingMyDaemonThread();
 
        // Setting thread named 't2' as our Daemon thread
        t2.setDaemon(true);
 
        // Starting all 3 threads using start() method
        t1.start();
        t2.start();
        t3.start();
 
        // Now start() will automatically
        // invoke run() method
    }
}

Java

// Java Program to show Working of Daemon Thread
// with users threads where start() is invoked
// prior before setting thread to Daemon
 
import java.io.*;
// Basically we are importing Thread class
// from java.util package
import java.util.*;
 
// Class 1
// Helper Class extending Thread class
class CheckingMyDaemonThread extends Thread {
 
    // Method
    // run() method which is invoked as soon as
    // thread start via start()
    public void run()
    {
 
        // Checking whether the thread is daemon thread or
        // not
        if (Thread.currentThread().isDaemon()) {
 
            // Print statement when Daemon thread is called
            System.out.println(
                "I am daemon thread and I am working");
        }
 
        else {
 
            // Print statement whenever users thread is
            // called
            System.out.println(
                "I am user thread and I am working");
        }
    }
}
 
// Class 2
// Main Class
class GFG {
 
    // Method
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating threads objects of above class
        // in the main body
        CheckingMyDaemonThread t1
            = new CheckingMyDaemonThread();
        CheckingMyDaemonThread t2
            = new CheckingMyDaemonThread();
        CheckingMyDaemonThread t3
            = new CheckingMyDaemonThread();
 
        // Starting all 3 threads using start() method
        t1.start();
        t2.start();
        t3.start();
 
        // Now start() will automatically invoke run()
        // method
 
        // Now at last setting already running thread 't2'
        // as our Daemon thread will throw an exception
        t2.setDaemon(true);
    }
}

Otra forma de lograr lo mismo es a través de Thread Group en el que, como su nombre indica, varios hilos se tratan como un solo objeto y luego todas las operaciones se llevan a cabo sobre este objeto, lo que ayuda a proporcionar un sustituto para Thread Pool.  

Nota: 

Al implementar ThreadGroup, tenga en cuenta que ThreadGroup es parte de la clase ‘ java.lang.ThreadGroup’ , no parte de la clase Thread en Java, así que eche un vistazo a los constructores y métodos de la clase ThreadGroup antes de seguir adelante, manteniendo un control sobre los métodos obsoletos en su clase para que para no enfrentar más ambigüedad. 

Aquí el método main() en sí mismo es un subproceso por el cual se ve una Excepción en main() mientras se ejecuta el programa debido a que a veces se lanza una excepción de subproceso system.main durante la ejecución del programa.

Sincronización

Es el mecanismo que limita el acceso de múltiples subprocesos para compartir un recurso común, por lo que se sugiere que sea útil cuando solo se otorga acceso a un subproceso a la vez.

Se implementa en el programa usando la palabra clave ‘ synchroned ‘.

Ahora analicemos finalmente algunas ventajas y desventajas de la sincronización antes de implementarla. Para una sincronización más profunda, también se puede aprender el bloqueo de nivel de objeto y el bloqueo de nivel de clase y notar las diferencias entre dos para obtener una comprensión justa de lo mismo antes de implementar lo mismo.

¿Por qué es necesaria la sincronización?

  1. Los problemas de inconsistencia de datos son el problema principal en el que múltiples subprocesos acceden a la memoria común, lo que a veces genera fallas para evitar que otro subproceso pase por alto un subproceso si falla.
  2. Integridad de los datos
  3. Para trabajar con un recurso compartido común que es muy esencial en el mundo real, como en los sistemas bancarios.

Nota: No elija la palabra clave sincronizada a menos que sea más necesaria, recuerde esto ya que no hay una configuración de prioridad para los subprocesos, por lo que si el subproceso principal se ejecuta antes o después de otro subproceso, la salida del programa sería diferente.

La mayor ventaja de la sincronización es el aumento de la resistencia idiota, ya que uno no puede elegir arbitrariamente un objeto para bloquear, como resultado, la string literal no puede bloquearse o ser el contenido. Por lo tanto, estas malas prácticas no son posibles de realizar en el bloque de método sincronizado.

Como hemos visto enormes ventajas y sabemos lo importante que es, pero conlleva una desventaja.

Desventaja: surgirán problemas de rendimiento ya que durante la ejecución de un subproceso, todos los demás subprocesos se ponen en un estado de bloqueo y tenga en cuenta que no están en estado de espera . Esto provoca una caída del rendimiento si el tiempo que tarda un subproceso es demasiado largo.

Count-Variable-Shared-Resource

Como se percibe en la imagen en la que obtenemos, la variable de conteo que se comparte, el recurso se actualiza aleatoriamente. Es debido a los subprocesos múltiples por lo que este concepto se convierte en una necesidad.

  • Caso 1: si el ‘ hilo principal’ se ejecuta primero, el conteo se incrementará seguido de un ‘ hilo T’ en sincronización
  • Caso 2: si el ‘ subproceso T ‘ se ejecuta primero, el conteo no se incrementará seguido por el ‘ subproceso principal ‘ en sincronización

Implementación: tomemos un programa de muestra para observar este conflicto de conteo 0 1

Ejemplo:

Java

// Java Program to illustrate Output Conflict between
// Execution of Main thread vs Thread created
 
// count = 1 if main thread executes first
// count = 1 if created thread executes first
 
// Importing basic required libraries
import java.io.*;
import java.util.*;
 
// Class 1
// Helper Class extending Thread class
class MyThread extends Thread {
 
    // Declaring and initializing initial count to zero
    int count = 0;
 
    // Method 1
    // To increment the count above by unity
    void increment() { count++; }
 
    // Method 2
    // run method for thread invoked after
    // created thread has started
    public void run()
    {
 
        // Call method in this method
        increment();
 
        // Print and display the count
        System.out.println("Count : " + count);
    }
}
 
// Class 2
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Creating the above our Thread class object
        // in the main() method
        MyThread t1 = new MyThread();
 
        // start() method to start execution of created
        // thread that will look for run() method
        t1.start();
    }
}

Producción:

Salida Explicación:

Aquí el conteo se incrementa a 1, lo que significa que ‘ hilo principal ‘ se ha ejecutado antes que ‘ hilo creado ‘. Lo hemos ejecutado muchas veces y compilado y ejecutado una vez más en todos los casos aquí el hilo principal se ejecuta más rápido que el hilo creado, pero recuerde que la salida puede variar. Nuestro subproceso creado puede ejecutarse antes del ‘ subproceso principal ‘, lo que lleva a ‘Recuento: 0’ como salida en la consola. 
 

Ahora, otro tema que surge al tratar con la sincronización en subprocesos es la seguridad de subprocesos en la sincronización de Java, es el nuevo concepto que surge en la sincronización, así que discutámoslo considerando

  • Un escenario de la vida real seguido de
  • Representación pictórica a modo de ilustración seguida de
  • Descripción técnica e implementación.

Escenario de la vida real 

Supongamos que una persona está retirando cierta cantidad de dinero del banco y, al mismo tiempo, la tarjeta de cajero automático registrada con el mismo número de cuenta está realizando una operación de retiro por parte de otro usuario. Ahora suponga que retirar una cantidad de dinero de la banca neta hace que los fondos en la cuenta sean menores que la cantidad que se debe retirar o viceversa. Esto hace que el banco sea inseguro ya que se debitan más fondos de la cuenta de los que realmente estaban presentes en la cuenta, lo que hace que el banco sea muy inseguro y no se ve en la vida diaria. Entonces, lo que hacen los bancos es que solo permiten una transacción a la vez. Una vez que termina, se permite otro.

Ilustración:

Interpretando la misma tecnología, ya que hay dos procesos diferentes en curso cuyo objeto en caso de ejecución paralela está encabezado por subprocesos. Ahora, poseer tales rasgos sobre hilos que deben cuidarse antes de la ejecución o, en palabras más simples, sincronizarlos. Este mecanismo se conoce como Thread Safe con el uso de la palabra clave ‘ synchroned ‘ antes del método/función compartida común que se realizará en paralelo. 

Descripción técnica: 

Como sabemos, Java tiene una característica, Multithreading , que es un proceso de ejecución de múltiples hilos simultáneamente. Cuando varios subprocesos están trabajando en los mismos datos y el valor de nuestros datos está cambiando, ese escenario no es seguro para subprocesos y obtendremos resultados inconsistentes. Cuando un subproceso ya está trabajando en un objeto y evita que otro subproceso trabaje en el mismo objeto, este proceso se denomina Thread-Safety. Ahora, hay varias formas de lograr la seguridad de subprocesos en nuestro programa, a saber, las siguientes:

  1. Uso de sincronización
  2. Uso de palabras clave volátiles
  3. Usando variable atómica
  4. Uso de la palabra clave final

Conclusión: por lo tanto, si estamos accediendo a un subproceso a la vez, podemos decir que el programa es seguro para subprocesos y si se accede a varios subprocesos, se dice que el programa no es seguro para subprocesos, es decir, un recurso a la vez no puede ser compartido por varios hilos a la vez.

Implementación:

  • Programa Java para ilustrar las iteraciones de subprocesos incompletas que devuelven el valor del contador a cero independientemente del límite de la iteración
  • Programa Java para ilustrar iteraciones completas de subprocesos que ilustran el método join()
  • Programa Java para ilustrar programas que no son seguros para subprocesos o que no se sincronizan a partir de iteraciones incompletas
  • Programa Java para ilustrar programas Thread Safe y sincronizados a partir de Iteraciones completas usando la palabra clave ‘ synchroned ‘.

Ejemplos

Java

// Example 1
// Java Program to illustrate Incomplete Thread Iterations
// Returning Counter Value to Zero
// irrespective of iteration bound
 
// Importing input output classes
import java.io.*;
 
// Class 1
// Helper Class
class TickTock {
 
    // Member variable of this class
    int count;
 
    // Method of this Class
    // It increments counter value whenever called
    public void increment()
    {
        // Increment count by unity
        // i.e count = count + 1;
        count++;
    }
    //
}
 
// Class 2
// Synchronization demo class
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of class TickTock in main()
        TickTock tt = new TickTock();
 
        // Now, creating an thread object
        // using Runnable interface
        Thread t1 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 10000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Making above thread created to start
        // via start() method which automatically
        // calls run() method in Ticktock class
        t1.start();
 
        // Print and display the count
        System.out.println("Count : " + tt.count);
    }
}

Java

// Example 2
// Java Program to Illustrate Complete Thread Iterations
// illustrating join() Method
 
// Importing input output classes
import java.io.*;
 
// Class 1
// Helper Class
class TickTock {
 
    // Member variable of this class
    int count;
 
    // Method of this Class
    public void increment()
    {
 
        // Increment count by unity
        // whenever this function is called
        count++;
    }
}
 
// Class 2
// Synchronization demo class
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of class TickTock in main()
        TickTock tt = new TickTock();
 
        // Now, creating an thread object
        // using Runnable interface
        Thread t1 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 1000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Making above thread created to start
        // via start() method which automatically
        // calls run() method
        t1.start();
 
        // Now we are making main() thread to wait so
        // that thread t1 completes it job
        // using join() method
        t1.join();
 
        // Print and display the count value
        System.out.println("Count : " + tt.count);
    }
}

Java

// Example 3
// Java Program to Illustrate Thread Unsafe Or
// Non-synchronizing Programs as of Incomplete Iteations
// Without using 'synchronized' program
 
// Importing input output classes
import java.io.*;
 
// Class 1
// Helper Class
class TickTock {
 
    // Member variable of this class
    int count;
 
    // Method of this Class
    public void increment()
    {
 
        // Increment count by unity
        count++;
    }
}
 
// Class 2
// Synchronization demo class
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of class TickTock in main()
        TickTock tt = new TickTock();
 
        // Now, creating an thread object
        // using Runnable interface
        Thread t1 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 100000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Now creating another thread and lets check
        // how they increment count value running parallelly
        // Thread 2
        Thread t2 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 100000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Making above thread created to start
        // via start() method which automatically
        // calls run() method
        t1.start();
        t2.start();
 
        // Now we are making main() thread to wait so
        // that thread t1 completes it job
        t1.join();
        t2.join();
 
        // Print and display the count
        System.out.println("Count : " + tt.count);
    }
}

Java

// Example 4
// Java Program to Illustrate Thread Safe And
// Synchronized Programs as of Complete Iteations
// using 'synchronized' Keyword
 
// Importing input output classes
import java.io.*;
 
// Class 1
// helper Class
class TickTock {
 
    // Member variable of this class
    int count;
 
    // Method of this Class
    public synchronized void increment()
    {
 
        // Increment count by unity
        count++;
    }
    //
}
 
// Class 2
// Synchronization demo class
// Main Class
class GFG {
 
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of class TickTock in main()
        TickTock tt = new TickTock();
 
        // Now, creating an thread object
        // using Runnable interface
        Thread t1 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 100000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Thread 2
        Thread t2 = new Thread(new Runnable() {
            // Method
            // To begin the execution of thread
            public void run()
            {
 
                // Expression
                for (int i = 0; i < 100000; i++) {
 
                    // Calling object of above class
                    // in main() method
                    tt.increment();
                }
            }
        });
 
        // Making above thread created to start
        // via start() method which automatically
        // calls run() method
        t1.start();
        t2.start();
 
        // Now we are making main() thread to wait so
        // that thread t1 completes it job
        t1.join();
        t2.join();
 
        // Print and display the count
        System.out.println("Count : " + tt.count);
    }
}

Producción:  

Caso 1 

Count : 0 

Caso 2 

Count : 10000 

Caso 3 

Count : 151138 

Caso 4 

Count : 200000

 Salida Explicación: 

En el caso 1, podemos ver que el conteo es cero como se inicializó. Ahora tenemos dos hilos hilo principal y el hilo t1. Entonces, hay dos hilos, así que ahora lo que sucede a veces es compartido entre ambos hilos.  

En el caso 1, ambos acceden a la variable de conteo donde estamos tratando de acceder directamente al subproceso a través del subproceso t1.count, que arrojará 0 siempre que necesitemos llamarlo con la ayuda del objeto para realizar la ejecución. 

Ahora hemos entendido que el funcionamiento de la sincronización es un hilo que no es más que un término Concurrencia en Java que, en lenguaje sencillo, ejecuta múltiples tareas. Representemos la concurrencia en subprocesos con la ayuda de una ilustración pictórica.

Considere la tarea de multiplicar una array de elementos por un multiplicador de 2. Ahora, si comenzamos a multiplicar cada elemento al azar, tomará una gran cantidad de tiempo, ya que cada vez que se busque el elemento en la computadora. Con mucho, hemos estudiado los subprocesos múltiples anteriormente en los que hemos concluido en una sola línea que el subproceso es la columna vertebral de los subprocesos múltiples. Entonces, incorporando subprocesos en la situación anterior, ya que la máquina es de cuatro núcleos, aquí tomamos 4 subprocesos para cada núcleo donde dividimos el conjunto de muestras de computación anterior en (1/4) lo que da como resultado una computación 4 veces más rápida. Si en el escenario anterior había tomado 4 segundos, ahora tomará solo 1 segundo. Este mecanismo de ejecución paralela de subprocesos para lograr cálculos más rápidos y sin retrasos se conoce como concurrencia.

Nota: Opte por subprocesos múltiples siempre para la ejecución concurrente y, si no usa este concepto, opte por la ejecución secuencial a pesar de tener fragmentos de código más grandes, ya que la seguridad de nuestro código es el problema principal.

Publicación traducida automáticamente

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