window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versión: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster ya existe') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Concurrencia en Java Parte 1 - Introducción - The Codest
The Codest
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Industrias
    • Fintech y Banca
    • E-commerce
    • Adtech
    • Tecnología sanitaria
    • Fabricación
    • Logística
    • Automoción
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
Flecha atrás VOLVER
2022-06-15
Desarrollo de software

Concurrencia en Java Parte 1 - Introducción

The Codest

Rafal Sawicki

Desarrollador Java

Lea la primera parte de nuestra serie de blogs dedicada a la concurrencia en Java. En el siguiente artículo vamos a echar un vistazo más de cerca a las diferencias entre hilos y procesos, pools de hilos, ejecutores y ¡mucho más!

En general, el enfoque de programación convencional es secuencial. Todo en un programa ocurre paso a paso.
Pero, de hecho, el paralelo es cómo funciona el mundo entero: es la capacidad de ejecutar más de una tarea simultáneamente.

Hilo conductor vs. proceso

Para debatir temas avanzados como concurrencia en Java o multihilo, tenemos que establecer algunas definiciones comunes para estar seguros de que estamos en la misma página.

Empecemos por lo básico. En el mundo no secuencial, tenemos dos tipos de representantes de concurrencia: procesos y
hilos. Un proceso es una instancia del programa en ejecución. Normalmente, está aislado de otros procesos.
El sistema operativo se encarga de asignar recursos a cada proceso. Además, actúa como un conductor que
los programa y los controla.

Un hilo es una especie de proceso pero a un nivel inferior, por lo que también se conoce como hilo ligero. Múltiples hilos pueden ejecutarse en un
proceso. Aquí el programa actúa como planificador y controlador de hilos. De esta forma los programas individuales parecen hacer
múltiples tareas al mismo tiempo.

La diferencia fundamental entre hilos y procesos es el nivel de aislamiento. El proceso tiene su propio conjunto de
recursos, mientras que el hilo comparte datos con otros hilos. Puede parecer un enfoque propenso a errores y, de hecho, lo es. En
Ahora, no vamos a centrarnos en eso, ya que está fuera del alcance de este artículo.

Procesos, hilos... Vale... Pero, ¿qué es exactamente la concurrencia? Concurrencia significa que se pueden ejecutar varias tareas al mismo tiempo.
tiempo. No significa que esas tareas tengan que ejecutarse simultáneamente: eso es el paralelismo. Concurrenc en Javay tampoco
requieren disponer de varias CPU o incluso de varios núcleos. Se puede conseguir en un entorno de un solo núcleo aprovechando
cambio de contexto.

Un término relacionado con la concurrencia es multithreading. Se trata de una característica de los programas que les permite ejecutar varias tareas a la vez. No todos los programas utilizan este enfoque, pero los que lo hacen pueden denominarse multihilo.

Ya casi estamos listos, sólo falta una definición. Asincronía significa que un programa realiza operaciones no bloqueantes.
Inicia una tarea y luego sigue con otras cosas mientras espera la respuesta. Cuando recibe la respuesta, puede reaccionar ante ella.

Todo ese jazz

Por defecto, cada Aplicación Java se ejecuta en un proceso. En ese proceso, hay un hilo relacionado con el main() método de
una aplicación. Sin embargo, como se ha mencionado, es posible aprovechar los mecanismos de múltiples hilos dentro de una
programa.

Ejecutable

Hilo es un Java en la que ocurre la magia. Esta es la representación del objeto del hilo antes mencionado. Para
crear su propio hilo, puede ampliar el Hilo clase. Sin embargo, no es un enfoque recomendable. Hilos como mecanismo que ejecuta la tarea. Las tareas son piezas de código que queremos ejecutar en modo concurrente. Podemos definirlos utilizando la función Ejecutable interfaz.

Pero basta de teoría, pongamos nuestro código donde está nuestra boca.

Problema

Supongamos que tenemos un par de matrices de números. Para cada matriz, queremos saber la suma de los números de una matriz. Supongamos
Imaginemos que hay muchas matrices de este tipo y que cada una de ellas es relativamente grande. En tales condiciones, queremos hacer uso de la concurrencia y sumar cada matriz como una tarea independiente.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Tarea ejecutable1 = () -> {
    int suma = Arrays.stream(a1).suma();
    System.out.println("1. La suma es: " + suma);
};

Tarea ejecutable2 = () -> {
    int suma = Arrays.stream(a2).suma();
    System.out.println("2. La suma es: " + suma);
};

Runnable tarea3 = () -> {
    int suma = Arrays.stream(a3).suma();
    System.out.println("3. La suma es: " + suma);
};

nuevo Thread(tarea1).start();
nuevo subproceso(tarea2).start();
new Thread(task3).start();

Como puede verse en el código anterior Ejecutable es una interfaz funcional. Contiene un único método abstracto ejecutar()
sin argumentos. La dirección Ejecutable debe ser implementada por cualquier clase cuyas instancias estén destinadas a ser
ejecutado por un hilo.

Una vez definida una tarea, puede crear un subproceso para ejecutarla. Esto puede hacerse mediante nuevo Hilo() constructor que
toma Ejecutable como argumento.

El último paso es iniciar() un hilo recién creado. En la API también existen ejecutar() métodos en Ejecutable y en
Hilo. Sin embargo, esa no es una forma de aprovechar la concurrencia en Java. Una llamada directa a cada uno de estos métodos resulta en
ejecutando la tarea en el mismo hilo que el main() se ejecuta el método.

Thread pools y Ejecutores

Cuando hay muchas tareas, crear un hilo separado para cada una no es una buena idea. Crear un Hilo es un
operación pesada y es mucho mejor reutilizar los hilos existentes que crear otros nuevos.

Cuando un programa crea muchos hilos de corta duración es mejor utilizar un pool de hilos. El pool de hilos contiene un número de
hilos listos para ejecutarse pero actualmente no activos. Dando un Ejecutable al pool hace que uno de los hilos llame a la función
ejecutar() método de dado Ejecutable. Tras completar una tarea, el hilo sigue existiendo y se encuentra en estado inactivo.

De acuerdo, ya lo has entendido: prefiere los thread pools a la creación manual. Pero, ¿cómo se pueden utilizar los thread pools? El Ejecutores
tiene una serie de métodos de fábrica estáticos para construir pools de hilos. Por ejemplo newCachedThredPool() crea
un pool en el que se crean nuevos hilos según sea necesario y los hilos inactivos se mantienen durante 60 segundos. Por el contrario,
newFixedThreadPool() contiene un conjunto fijo de hilos, en el que los hilos ociosos se mantienen indefinidamente.

Veamos cómo podría funcionar en nuestro ejemplo. Ahora no necesitamos crear hilos manualmente. En su lugar, tenemos que crear
Servicio de ejecutor que proporciona un pool de hilos. A continuación, podemos asignarle tareas. El último paso es cerrar el hilo
para evitar pérdidas de memoria. El resto del código anterior permanece igual.

ExecutorService executor = Executors.newCachedThreadPool();

executor.submit(task1);
executor.submit(task2);
executor.submit(task3);

ejecutor.shutdown();

Llamable

Ejecutable parece una forma ingeniosa de crear tareas concurrentes, pero tiene un defecto importante. No puede devolver ningún
valor. Es más, no podemos determinar si una tarea está terminada o no. Tampoco sabemos si se ha completado
normal o excepcional. La solución a esos males es Llamable.

Llamable es similar a Ejecutable en cierto modo también envuelve tareas asíncronas. La principal diferencia es que es capaz de
devuelven un valor. El valor devuelto puede ser de cualquier tipo (no primitivo), ya que la función Llamable es un tipo parametrizado.
Llamable es una interfaz funcional que tiene llamar() que puede lanzar un Excepción.

Ahora vamos a ver cómo podemos aprovechar Llamable en nuestro problema de matrices.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Callable task1 = () -> Arrays.stream(a1).sum();
Callable task2 = () -> Arrays.stream(a2).sum();
Callable task3 = () -> Arrays.stream(a3).sum();

ExecutorService executor = Executors.newCachedThreadPool();
Futuro future1 = executor.submit(task1);
Futuro future2 = executor.submit(task2);
Future future3 = executor.submit(task3);

System.out.println("1. La suma es: " + future1.get());
System.out.println("2. La suma es: " + future2.get());
System.out.println("3. La suma es: " + future3.get());

ejecutor.shutdown();

Bien, podemos ver cómo Llamable y se envía a Servicio de ejecutor. Pero, ¿qué diablos es Futuro?
Futuro actúa como puente entre hilos. La suma de cada matriz se produce en un hilo separado y necesitamos una forma de
devolver esos resultados a main().

Para recuperar el resultado de Futuro necesitamos llamar a get() método. Aquí puede ocurrir una de dos cosas. En primer lugar, el
resultado del cálculo realizado por Llamable está disponible. Entonces lo obtenemos inmediatamente. En segundo lugar, el resultado no es
listo todavía. En ese caso get() se bloqueará hasta que el resultado esté disponible.

Futuro computable

El problema con Futuro es que funciona en el "paradigma push". Cuando se utiliza Futuro tienes que ser como un jefe que
pregunta constantemente: '¿Está hecha la tarea? ¿Está lista?" hasta que ofrezca un resultado. Actuar bajo una presión constante es
caro. Mucho mejor sería pedir Futuro qué hacer cuando esté preparado para su tarea. Desgraciadamente,
Futuro no puede hacerlo sino Futuro computable puede.

Futuro computable funciona en el "paradigma pull". Podemos decirle qué hacer con el resultado cuando haya completado sus tareas. En
es un ejemplo de enfoque asíncrono.

Futuro computable funciona perfectamente con Ejecutable pero no con Llamable. En su lugar, es posible suministrar una tarea a
Futuro computable en forma de Proveedor.

Veamos cómo se relaciona lo anterior con nuestro problema.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println);

Lo primero que llama la atención es lo corta que es esta solución. Además, también tiene un aspecto limpio y ordenado.

Tarea a CompletableFuture puede ser proporcionada por supplyAsync() que toma Proveedor o por runAsync() que
toma Ejecutable. Una llamada de retorno - un fragmento de código que debe ejecutarse al finalizar la tarea - se define mediante thenAccept()
método.

Conclusiones

Java proporciona una gran cantidad de enfoques diferentes para la concurrencia. En este artículo, apenas hemos tocado el tema.

No obstante, hemos cubierto los aspectos básicos de Hilo, Ejecutable, Llamabley CallableFuture que plantea una buena cuestión
para seguir investigando el tema.

Artículos relacionados

Desarrollo de software

9 errores que hay que evitar al programar en Java

¿Qué errores deben evitarse al programar en Java? En el siguiente artículo responderemos a esta pregunta.

The Codest
Rafal Sawicki Desarrollador Java
Soluciones para empresas y escalas

La forma correcta de encontrar los mejores desarrolladores Java

Encontrar al desarrollador Java perfecto puede ser una tarea desalentadora. Dado que la demanda del mercado de este tipo de profesionales crece a un ritmo asombroso, las fuentes disponibles para la búsqueda de talentos a veces pueden parecer...

The Codest
Grzegorz Rozmus Jefe de unidad Java
Soluciones para empresas y escalas

10 empresas de Dubai que merece la pena vigilar en 2020

Dubai es el corazón de los Emiratos Árabes Unidos, con su cada vez más próspero mercado de empresas globales y prometedoras startups. Muchas pueden presumir de su éxito internacional y sus notables productos....

Atún Pinar
Soluciones para empresas y escalas

¿Cómo puede Java ayudar a su empresa?

Antes de empezar, me gustaría recordarte una cosa importante. Java no es sólo un lenguaje de programación.

Bartlomiej Kuczynski
Soluciones para empresas y escalas

Un día en la vida de un programador en The Codest

Podría sospechar que los horarios de trabajo de los programadores no difieren entre sí. Pero no es cierto. Cada startup, cada empresa de software e incluso cada...

The Codest
Pawel Rybczynski Software Engineer

Suscríbase a nuestra base de conocimientos y manténgase al día de la experiencia del sector informático.

    Quiénes somos

    The Codest - Empresa internacional de desarrollo de software con centros tecnológicos en Polonia.

    Reino Unido - Sede central

    • Oficina 303B, 182-184 High Street North E6 2JA
      Londres, Inglaterra

    Polonia - Centros tecnológicos locales

    • Parque de oficinas Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Embajada del Cerebro, Konstruktorska
      11, 02-673 Varsovia, Polonia

      The Codest

    • Inicio
    • Quiénes somos
    • Servicios
    • Case Studies
    • Saber cómo
    • Carreras profesionales
    • Diccionario

      Servicios

    • Asesoramiento
    • Desarrollo de software
    • Desarrollo backend
    • Desarrollo Frontend
    • Staff Augmentation
    • Desarrolladores de backend
    • Ingenieros de la nube
    • Ingenieros de datos
    • Otros
    • Ingenieros de control de calidad

      Recursos

    • Hechos y mitos sobre la cooperación con un socio externo de desarrollo de software
    • De EE.UU. a Europa: ¿Por qué las startups estadounidenses deciden trasladarse a Europa?
    • Comparación de los polos de desarrollo de Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filipinas), Eurasia (Turquía)
    • ¿Cuáles son los principales retos de los CTO y los CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condiciones de uso del sitio web

    Copyright © 2025 por The Codest. Todos los derechos reservados.

    es_ESSpanish
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean nl_NLDutch etEstonian elGreek es_ESSpanish