JavaRush /Blog Java /Random-ES /Pausa para el café #113. 5 cosas que probablemente no sab...

Pausa para el café #113. 5 cosas que probablemente no sabías sobre el multiproceso en Java. Diez extensiones de JetBrains para luchar contra la deuda técnica

Publicado en el grupo Random-ES

5 cosas que probablemente no sabías sobre subprocesos múltiples en Java

Fuente: DZone Thread es el corazón del lenguaje de programación Java. Incluso ejecutar el programa Hello World necesita el hilo principal. Si es necesario, podemos agregar otros subprocesos al programa si queremos que el código de nuestra aplicación sea más funcional y eficaz. Si hablamos de un servidor web, procesa simultáneamente cientos de solicitudes al mismo tiempo. Para ello se utilizan varios subprocesos. Pausa para el café #113.  5 cosas que probablemente no sabías sobre el multiproceso en Java.  Diez extensiones de JetBrains para combatir la deuda técnica - 1Los hilos son sin duda útiles, pero trabajar con ellos puede resultar complicado para muchos desarrolladores. En este artículo, compartiré cinco conceptos de subprocesos múltiples que quizás los desarrolladores nuevos y experimentados no conozcan.

1. El orden del programa y el orden de ejecución no coinciden

Cuando escribimos código, asumimos que se ejecutará exactamente como lo escribimos. Sin embargo, en realidad este no es el caso. El compilador de Java puede cambiar el orden de ejecución para optimizarlo si puede determinar que la salida no cambiará en el código de un solo subproceso. Mire el siguiente fragmento de código:
package ca.bazlur.playground;

import java.util.concurrent.Phaser;

public class ExecutionOrderDemo {
    private static class A {
        int x = 0;
    }

    private static final A sharedData1 = new A();
    private static final A sharedData2 = new A();

    public static void main(String[] args) {
        var phaser = new Phaser(3);
        var t1 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l1 = sharedData1;
            var l2 = l1.x;
            var l3 = sharedData2;
            var l4 = l3.x;
            var l5 = l1.x;
            System.out.println("Thread 1: " + l2 + "," + l4 + "," + l5);
        });
        var t2 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l6 = sharedData1;
            l6.x = 3;
            System.out.println("Thread 2: " + l6.x);
        });
        t1.start();
        t2.start();
        phaser.arriveAndDeregister();
    }
}
Este código parece simple. Tenemos dos instancias de datos compartidos ( sharedData1 y SharedData2 ) que utilizan dos subprocesos. Cuando ejecutamos el código, esperamos que el resultado sea así:
Hilo 2: 3 Hilo 1: 0,0,0
Pero si ejecuta el código varias veces, verá un resultado diferente:
Hilo 2: 3 Hilo 1: 3,0,3 Hilo 2: 3 Hilo 1: 0,0,3 Hilo 2: 3 Hilo 1: 3,3,3 Hilo 2: 3 Hilo 1: 0,3,0 Hilo 2 : 3 Hilo 1: 0,3,3
No estoy diciendo que todas estas transmisiones se reproducirán exactamente así en tu máquina, pero es completamente posible.

2. El número de subprocesos Java es limitado.

Crear un hilo en Java es fácil. Sin embargo, esto no significa que podamos crear tantos como queramos. El número de hilos es limitado. Podemos averiguar fácilmente cuántos subprocesos podemos crear en una máquina en particular usando el siguiente programa:
package ca.bazlur.playground;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Playground {
    public static void main(String[] args) {
        var counter = new AtomicInteger();
        while (true) {
            new Thread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            }).start();
        }
    }
}
El programa anterior es muy simple. Crea un hilo en un bucle y luego lo estaciona, lo que significa que el hilo está deshabilitado para uso futuro pero realiza una llamada al sistema y asigna memoria. El programa continúa creando subprocesos hasta que no puede crear más y luego genera una excepción. Nos interesa el número que recibiremos hasta que el programa arroje una excepción. En mi computadora solo pude crear 4065 hilos.

3. Demasiados subprocesos no garantizan un mejor rendimiento

Es ingenuo creer que facilitar los subprocesos en Java mejorará el rendimiento de la aplicación. Desafortunadamente, esta suposición es errónea con nuestro modelo tradicional de subprocesos múltiples que Java proporciona hoy. De hecho, demasiados subprocesos pueden reducir el rendimiento de una aplicación. Primero hagamos esta pregunta: ¿cuál es la cantidad máxima óptima de subprocesos que podemos crear para maximizar el rendimiento de la aplicación? Bueno, la respuesta no es tan sencilla. Depende mucho del tipo de trabajo que hagamos. Si tenemos múltiples tareas independientes, todas las cuales son computacionales y no bloquean ningún recurso externo, entonces tener una gran cantidad de subprocesos no mejorará mucho el rendimiento. En cambio, si tenemos un procesador de 8 núcleos el número óptimo de hilos podría ser (8+1). En tal caso, podemos confiar en el subproceso paralelo introducido en Java 8. De forma predeterminada, el subproceso paralelo utiliza el grupo compartido Fork/Join. Crea subprocesos iguales a la cantidad de procesadores disponibles, lo que es suficiente para que funcionen intensamente. Agregar más subprocesos a un trabajo que consume mucha CPU y donde no hay nada bloqueado no mejorará el rendimiento. Más bien, simplemente desperdiciaremos recursos. Nota. La razón para tener un subproceso adicional es que incluso un subproceso de cómputo intensivo a veces causa un error en la página o se suspende por algún otro motivo. (Ver: Paralelismo de Java en la práctica , Brian Goetz, página 170) Sin embargo, supongamos, por ejemplo, que las tareas están vinculadas a E/S. En este caso, dependen de la comunicación externa (por ejemplo, base de datos, otras API), por lo que tiene sentido una mayor cantidad de subprocesos. La razón es que cuando un hilo está esperando en la API Rest, otros hilos pueden continuar trabajando. Ahora podemos preguntarnos nuevamente, ¿cuántos hilos son demasiados para tal caso? Depende. No existen números perfectos que se ajusten a todos los casos. Por lo tanto, debemos realizar pruebas adecuadas para descubrir qué funciona mejor para nuestra carga de trabajo y aplicación específicas. En el escenario más típico, normalmente tenemos un conjunto mixto de tareas. Y en tales casos las cosas llegan a su fin. En su libro "Java Concurrency in Practice", Brian Goetz propuso una fórmula que podemos utilizar en la mayoría de los casos. Número de subprocesos = Número de núcleos disponibles * (1 + Tiempo de espera / Tiempo de servicio) El tiempo de espera puede ser IO, como esperar una respuesta HTTP, adquirir un bloqueo, etc. Tiempo de servicio(Tiempo de servicio) es el tiempo de cálculo, como el procesamiento de una respuesta HTTP, la clasificación/desclasificación, etc. Por ejemplo, una aplicación llama a una API y luego la procesa. Si tenemos 8 procesadores en el servidor de aplicaciones, el tiempo de respuesta API promedio es de 100 ms y el tiempo de procesamiento de respuesta es de 20 ms, entonces el tamaño de hilo ideal sería:
norte = 8 * (1 + 100/20) = 48
Sin embargo, esto es una simplificación excesiva; Las pruebas adecuadas siempre son fundamentales para determinar el número.

4. Multithreading no es paralelismo

A veces utilizamos indistintamente subprocesos múltiples y paralelismo, pero esto ya no es del todo relevante. Aunque en Java logramos ambas cosas usando un hilo, son dos cosas diferentes. “En programación, el multiproceso es un caso especial independientemente de los procesos que se ejecuten, y el paralelismo es la ejecución simultánea de cálculos (posiblemente relacionados). El multiproceso consiste en interactuar con muchas cosas al mismo tiempo. La concurrencia es hacer muchas cosas al mismo tiempo”. La definición anterior dada por Rob Pike es bastante precisa. Digamos que tenemos tareas completamente independientes y se pueden calcular por separado. En este caso, estas tareas se denominan paralelas y se pueden ejecutar con un grupo Fork/Join o un subproceso paralelo. En cambio, si tenemos muchas tareas, algunas de ellas pueden depender de otras. La forma en que componemos y estructuramos se llama subprocesos múltiples. Tiene que ver con la estructura. Es posible que queramos realizar varias tareas simultáneamente para lograr un resultado determinado, sin necesariamente terminar una más rápido.

5. Project Loom nos permite crear millones de hilos

En el punto anterior, sostuve que tener más subprocesos no significa un mejor rendimiento de la aplicación. Sin embargo, en la era de los microservicios, interactuamos con demasiados servicios para realizar un trabajo específico. En tal escenario, los subprocesos permanecen bloqueados la mayor parte del tiempo. Si bien un sistema operativo moderno puede manejar millones de sockets abiertos, no podemos abrir muchos canales de comunicación ya que estamos limitados por la cantidad de subprocesos. Pero, ¿qué pasa si creas millones de subprocesos y cada uno de ellos utiliza un socket abierto para comunicarse con el mundo exterior? Esto definitivamente mejorará el rendimiento de nuestra aplicación. Para respaldar esta idea, existe una iniciativa en Java llamada Project Loom. Usándolo, podemos crear millones de hilos virtuales. Por ejemplo, usando el siguiente fragmento de código, pude crear 4,5 millones de subprocesos en mi máquina.
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Main {
    public static void main(String[] args) {
        var counter = new AtomicInteger();

        // 4_576_279
        while (true) {
            Thread.startVirtualThread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            });
        }
    }
}
Para ejecutar este programa debes tener instalado Java 18, que puedes descargar aquí . Puede ejecutar el código usando el siguiente comando: java --source 18 --enable-preview Main.java

Diez extensiones de JetBrains para luchar contra la deuda técnica

Fuente: DZone Muchos equipos de desarrollo sienten una enorme presión para cumplir con los plazos. Debido a esto, a menudo no tienen tiempo suficiente para arreglar y limpiar su código base. A veces, en estas situaciones, la deuda técnica se acumula rápidamente. Las extensiones del editor pueden ayudar a resolver este problema. Echemos un vistazo a las 10 mejores extensiones de JetBrains para combatir la deuda técnica (con soporte para Java). Pausa para el café #113.  5 cosas que probablemente no sabías sobre el multiproceso en Java.  Diez extensiones de JetBrains para luchar contra la deuda técnica - 2

Herramientas de refactorización y deuda técnica

1. Refactorización de información

RefactorInsight mejora la visibilidad de los cambios de código en el IDE al proporcionar información sobre refactorizaciones.
  1. La extensión define refactorizaciones en solicitudes de fusión.
  2. Marca confirmaciones que contienen refactorizaciones.
  3. Ayuda a ver la refactorización de cualquier confirmación específica seleccionada en la pestaña Registro de Git.
  4. Muestra el historial de refactorización de clases, métodos y campos.

2. Seguimiento de problemas de tamaño de paso en IDE

Stepsize es un excelente rastreador de problemas para desarrolladores. La extensión ayuda a los ingenieros no sólo a crear mejores TODOs y comentarios de código, sino también a priorizar la deuda técnica, las refactorizaciones y similares:
  1. Stepsize te permite crear y ver tareas en código directamente en el editor.
  2. Encuentre problemas que afecten las funciones en las que está trabajando.
  3. Agregue problemas a sus sprints utilizando integraciones de Jira, Asana, Linear, Azure DevOps y GitHub.

3. Nueva reliquia CodeStream

New Relic CodeStream es una plataforma de colaboración de desarrolladores para discutir y revisar código. Admite solicitudes de extracción de GitHub, BitBucket y GitLab, gestión de problemas de Jira, Trello, Asana y otros 9, y proporciona discusiones sobre código, uniéndolo todo.
  1. Cree, revise y combine solicitudes de extracción en GitHub.
  2. Obtenga comentarios sobre el trabajo en progreso con revisiones preliminares del código.
  3. Discutir problemas de código con compañeros de equipo.

TODO y comentarios

4. Resaltador de comentarios

Este complemento le permite crear resaltado personalizado de líneas de comentarios y palabras clave del idioma. El complemento también tiene la capacidad de definir tokens personalizados para resaltar líneas de comentarios.

5. Mejores comentarios

La extensión Better Comments le ayuda a crear comentarios más claros en su código. Con esta extensión podrás clasificar tus anotaciones en:
  1. Alertas.
  2. Peticiones.
  3. HACER.
  4. Momentos básicos.

Errores y vulnerabilidades de seguridad.

6.SonarLint _

SonarLint le permite solucionar problemas de código antes de que surjan. También se puede utilizar como corrector ortográfico. SonarLint destaca errores y vulnerabilidades de seguridad a medida que codifica, con instrucciones claras de solución para que pueda solucionarlos antes de que se confirme el código.

7. Detectar errores

El complemento SpotBugs proporciona análisis de código de bytes estático para encontrar errores en el código Java de IntelliJ IDEA. SpotBugs es una herramienta de detección de defectos para Java que utiliza análisis estático para encontrar más de 400 patrones de errores, como desreferencias de puntero nulo, bucles recursivos infinitos, uso indebido de bibliotecas de Java y puntos muertos. SpotBugs puede identificar cientos de defectos graves en aplicaciones grandes (normalmente alrededor de 1 defecto por cada 1000-2000 líneas de declaraciones sin comentar).

8. Escáner de vulnerabilidades Snyk

El escáner de vulnerabilidades de Snyk lo ayuda a encontrar y solucionar vulnerabilidades de seguridad y problemas de calidad del código en sus proyectos.
  1. Encontrar y solucionar problemas de seguridad.
  2. Vea una lista de diferentes tipos de problemas, divididos en categorías.
  3. Muestra sugerencias para la solución de problemas.

9. Localizador de caracteres de ancho cero

Este complemento mejora la verificación y detección de errores difíciles de encontrar relacionados con caracteres invisibles de ancho cero en el código fuente y los recursos. Al usarlo, asegúrese de que la verificación "Carácter Unicode de ancho cero" esté habilitada.

10.CódigoMR _

CodeMR es una herramienta de análisis de código estático y de calidad del software que ayuda a las empresas de software a desarrollar mejores códigos y programas. CodeMR visualiza métricas de código y atributos de calidad de alto nivel (acoplamiento, complejidad, cohesión y tamaño) en varias vistas, como estructura de paquete, mapa de árbol, rayos solares, dependencia y vistas gráficas.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION