{"id":20094623,"url":"https://github.com/alansastre/java-concurrency","last_synced_at":"2026-02-27T22:01:56.057Z","repository":{"id":152300360,"uuid":"582599427","full_name":"alansastre/java-concurrency","owner":"alansastre","description":"Concurrencia en Java","archived":false,"fork":false,"pushed_at":"2022-12-27T10:23:00.000Z","size":9,"stargazers_count":1,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-13T16:52:32.336Z","etag":null,"topics":["async","concurrency","java","java11","java17","java8","polling","thread-management","threads","threadsafe","wait"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alansastre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-27T10:22:56.000Z","updated_at":"2024-09-26T08:57:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"ff0047f4-b8c3-44b2-baab-dc4600c1adf3","html_url":"https://github.com/alansastre/java-concurrency","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjava-concurrency","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjava-concurrency/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjava-concurrency/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjava-concurrency/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alansastre","download_url":"https://codeload.github.com/alansastre/java-concurrency/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233689511,"owners_count":18714686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","concurrency","java","java11","java17","java8","polling","thread-management","threads","threadsafe","wait"],"created_at":"2024-11-13T16:51:28.038Z","updated_at":"2025-09-20T21:31:53.407Z","avatar_url":"https://github.com/alansastre.png","language":"Java","readme":"\n# Concurrencia en Java\n\n\nUn hilo (Thread) es la unidad de ejecución más pequeña que puede programar el sistema operativo.\n\nUn proceso es un grupo de hilos asociados que se ejecutan en el mismo entorno compartido (shared\nenvironment).\n\nUn proceso single-threaded es aquel proceso que contiene exactamente un hilo.\n\nUn proceso multi-threaded es aquel proceso admite más de un hilo.\n\nEntorno compartido significa que los hilos en el mismo proceso comparten el mismo\nespacio de memoria y pueden comunicarse directamente entre sí.  Por ejemplo: variables static, variables de instancia y locales pasadas a un hilo.\n\nUna tarea (Task) es una sola unidad de trabajo realizada por un hilo.\n\nUn hilo puede completar varias tareas independientes, pero solo una tarea a la vez.\n\nLa propiedad de ejecutar múltiples hilos y procesos al mismo tiempo se conoce como concurrencia.\n\n¿Cómo decide el sistema qué ejecutar cuando hay más hilos disponibles que CPU?\n\nLos sistemas operativos utilizan un programador de hilos (Thread scheduler) para determinar qué hilos se deben ejecutar actualmente.\n\nPor ejemplo, un thread scheduler puede emplear un programa round-robin en el que cada hilo disponible recibe el mismo número de ciclos de CPU para ejecutarse, con hilos visitados en orden circular.\n\nCuando se completa el tiempo asignado de un hilo, pero el hilo no ha terminado de procesarse, se produce un cambio de contexto.\n\nUn cambio de contexto es el proceso de almacenar el estado actual de un hilo y luego restaurar el estado del hilo para continuar la ejecución.\n\nEl cambio de contexto tiene asociado un coste debido a la pérdida de tiempo y la necesidad de recargar el estado de un hilo.\n\nSe debe minimizar la cantidad de cambios de contexto mientras mantienen una aplicación funcionando sin problemas.\n\nUn hilo puede interrumpir o reemplazar a otro hilo si tiene una prioridad de hilo más alta que el otro hilo.\n\nUna prioridad de hilo es un valor numérico asociado con un hilo que el planificador de hilos (Thread scheduler) tiene en cuenta al determinar qué hilos deben ejecutarse actualmente. En Java, las prioridades de hilos se especifican como valores enteros.\n\n## Tipos de hilos\n\n* Hilos de sistema: La máquina virtual de Java (JVM) crea un hilo y se ejecuta en segundo plano de la aplicación. Por ejemplo, el recolector de basura (garbage collector) es administrada por un hilo del sistema creado por la JVM. Por defecto son hilos daemon, es decir, que si no hay nada más pendiente de ejecución entonces el programa se cierra una vez se terminen.\n\n* Hilos definidos por el usuario: es uno creado por el desarrollador de la aplicación para realizar una tarea específica. No son hilos daemon, por tanto el programa espera a que terminen su trabajo. Se puede convertir en hilos daemon con el método ``.setDaemon(true)``.\n\n\n## Ciclo de vida de los hilos\n\n\nUn hilo puede pasar por varios estados durante su ciclo de vida. El método ``getState()`` de Thread devuelve una constante de enumeración que indica el estado actual del hilo, que cae en uno de los siguientes valores:\n\n* **NEW**\n- **RUNNABLE**\n- **BLOCKED**\n- **WAITING**\n- **TIMED_WAITING**\n- **TERMINATED**\n\nEstas constantes de enumeración se definen en la enumeración ``Thread.State`` y significan lo siguiente:\n\n* NEW: cuando se crea un hilo pero no se ha ejecutado (no se ha invocado el método ``start()``), está en este estado.\n\n\n* RUNNABLE: cuando se ha invocado el método ``start()``, el hilo entra en el estado ejecutable y su método ``run()`` se está ejecutando. se debe tener en cuenta que el hilo puede volver al estado ejecutable desde otro estado (en espera, bloqueado), pero es posible que el scheduler de hilos no lo seleccione de inmediato, de ahí el término \"ejecutable\", no en ejecución.\n\n\n* BLOCKED: cuando un hilo intenta adquirir un bloqueo intrínseco (no un bloqueo en el paquete java.util.concurrent) que actualmente está en manos de otro hilo, se bloquea. Cuando todos los demás hilos han renunciado al bloqueo y el planificador de hilos ha permitido que este hilo mantenga el bloqueo, el hilo se desbloquea y entra en el estado ejecutable.\n\n\n* WAITING: un hilo entra en este estado si espera a que otro hilo le notifique, lo cual es el resultado de llamar a Object.wait() o Thread.join(). El hilo también entra en estado de espera si espera un Bloqueo o una Condición en el paquete java.util.concurrent. Cuando otro hilo llama al método notify()/notifyAll() de Object o a signal()/signalAll() de Condition, el hilo vuelve al estado ejecutable.\n\n\n* TIMED_WAITING: un hilo entra en este estado si se llama a un método con parámetro de tiempo de espera: sleep(), wait(), join(), Lock.tryLock() y Condition.await(). El hilo sale de este estado si se agota el tiempo de espera o si se ha recibido la notificación adecuada.\n\n\n* TERMINATED: un hilo entra en estado terminado cuando ha completado la ejecución. El subproceso termina por una de dos razones:\n\n    + el método run() sale termina exitosamente.\n    + el método run() sale termina de forma abrupta debido a que ocurre una excepción no detectada.\n\n\n## Polling\n\nAunque la programación de multihilo permite ejecutar varias tareas al mismo tiempo, es común que un hilo necesite esperar a obtener los resultados de otro hilo.\n\nUna solución es utilizar el polling, es el proceso de verificar datos de forma intermitente en un intervalo fijo.\n\nMétodos para pausar, parar, esperar:\n\n* ``sleep()``\n* ``interrupt()``\n* ``isAlive()``\n* ``getState()``\n* ``join()``\n\n## API Concurrency\n\n``java.util.concurrent``\n\nAl escribir programas de hilos múltiples en la práctica, es mejor usar la API de concurrencia en lugar de trabajar con objetos de hilos directamente.\n\nLa interfaz Callable a menudo es preferible a Runnable, ya que permite recuperar fácilmente más detalles de la tarea una vez que se completa.\n\n\n### Pool de hilos\n\nUn pool de hilos es un grupo de hilos reutilizables instanciados previamente que están disponibles para realizar un conjunto de tareas arbitrarias.\n\n1. Se puede crear un **executor de un solo hilo**:\n\n``ExecutorService executor = Executors.newSingleThreadExecutor();``\n\nLos resultados se procesan secuencialmente en el orden en que se envían.\n\n2. Se puede crear un **executor de un pool de hilos**:\n\n``ExecutorService executor = Executors.newFixedThreadPool(10);``\n\nLos resultados se procesan paralelamente siempre que el executor no tenga todos los hilos ocupados.\n\n\n## Sincronización\n### volatile\n\nLa palabra clave ``volatile`` se usa para garantizar que el acceso a los datos dentro de la memoria sea consistente.\n\nUn atributo con el modificador ``volatile`` garantiza que solo un hilo esté modificando esa variable a la vez y que los datos leídos entre varios hilos sean coherentes.\n\nEn la práctica, rara vez se usa ``volatile``.\n\nPuede dar problemas ya que dependiendo de cómo se accede o modifica una variable entonces será thread-safe o no, por ejemplo:\n\n``private volatile int age = 0;``\n\nEl siguiente código es thread-safe:\n\n``age = age + 1;``\n\nPero el siguiente código no es thread-safe debido a que el operador de incremento ++ no es thread-safe para hilos, incluso cuando se usa ``volatile``. Esto se produce porque la operación ++ no es atómica, realizando dos tareas, lectura y escritura, que pueden ser interrumpidas por otros subprocesos.\n\n``++age;``\n\n### Operaciones atómicas\n\n**Atomicidad** es la propiedad de una operación para llevarse a cabo como una sola unidad de ejecución sin ninguna interferencia de otro hilo.\n\nUna versión atómica segura para hilos del operador de incremento realizaría la lectura y escritura de la variable como una sola operación, sin permitir que ningún otro subproceso acceda a la variable durante la operación.\n\nEl paquete ``java.util.concurrent.atomic`` contiene clases atómicas para distintos tipos de datos, por ejemplo:\n\n* ``AtomicBoolean``\n* ``AtomicInteger``\n* ``AtomicLong``\n\nEstas clases contienen métodos especiales que se comportan como una sola unidad de trabajo, por ejemplo: ``incrementAndGet()``.\n\nSi bien las clases atómicas ayudan a proteger una sola variable, no son útiles si se necesita ejecutar una serie de comandos o llamar a un método. No podemos usarlas para actualizar dos variables atómicas al mismo tiempo.\n\n\n### synchronized\n\nLa técnica más común para sincronizar el acceso es usar un monitor. Un monitor, también llamado **bloqueo** o **lock**, es una estructura que admite la exclusión mutua, que es la propiedad de que, como máximo, un hilo está ejecutando un segmento particular de código en un momento dado.\n\nEn Java, cualquier objeto se puede usar como monitor, junto con la palabra clave ``synchronized``.\n\nLa palabra ``synchronized`` puede ser aplicada también a un bloque entero de código.\n\nCada hilo que llega primero verificará si algún otro hilo ya está ejecutando el bloque.\n\nSi el bloqueo no está disponible, el hilo pasará a un estado BLOCKED hasta que pueda \"adquirir el bloqueo\".\n\nSi el bloqueo está disponible (o el hilo ya tiene el bloqueo), el único hilo ingresará al bloque, evitando que todos los demás hilos accedan. Una vez que el hilo termina de ejecutar el bloque, liberará el bloqueo, permitiendo que uno de los hilos en espera continúe.\n\nPara sincronizar el acceso a través de varios hilos, cada hilo debe tener acceso al mismo objeto. Si cada hilo se sincroniza en diferentes objetos, el código no es seguro para hilos (thread-safe).\n\n``synchronized`` también puede ser aplicado a métodos.\n\nUn bloque ``synchronized`` solo admite un conjunto limitado de funciones. Por ejemplo, ¿Qué pasa si queremos comprobar si hay un bloqueo disponible y, en caso contrario, realizar alguna otra tarea?\n\nAdemás, si el bloqueo nunca está disponible y lo sincronizamos, podríamos esperar todo el tiempo, produciendo así un **deadlock**. Un deadlock es una situación en la que el progreso de un sistema se detiene ya que cada hilo está esperando adquirir un recurso en poder de algún otro hilo.\n\n\n### Lock Framework\n\nUna solución más avanzada a `synchronized` es usar Lock framework.\n\nLa API de concurrencia de java incluye la interfaz ``Lock``, que es conceptualmente similar a usar la palabra clave `synchronized` pero con muchas más funcionalidades.\n\nSin embargo, en lugar de sincronizar en cualquier objeto, podemos \"bloquear\" solo en un objeto que implemente la interfaz ``Lock``.\n\nEjemplo con ``synchronized``:\n\n```java\n// Implementation #1 with a synchronized block \nObject object = new Object(); \nsynchronized(object) { \n\t// Protected code\n\t// ....\n}\n```\n\nEjemplo equivalente con Framework Lock:\n\n\n```java\n// Implementation #2 with a Lock \nLock lock = new ReentrantLock(); \n\ntry {\n\tlock.lock(); \n\t// Protected code\n\t// ...\n} finally {\n\tlock.unlock(); \n}\n```\n\n\n### CyclicBarrier\n\nA mayores de las técnicas anteriores, surge la necesidad de orquestar tareas complejas en múltiples pasos, teniendo en cuenta escenarios thread-safe.\n\nLa clase ``CyclicBarrier`` es una ayuda de sincronización que permite que un conjunto de hilos esperen unos a otros para alcanzar un punto de barrera común.\n\nLos CyclicBarriers son útiles en programas que involucran un grupo de hilos de tamaño fijo que ocasionalmente deben esperar el uno al otro. La barrera se llama cíclica porque se puede reutilizar después de que se liberan los hilos en espera.\n\nEsta solución es superior a una solución de hilo único, ya que las tareas individuales, pueden ser realizadas en paralelo.\n\n``CyclicBarrier`` toma en sus constructores un valor límite, indicando el número de hilos a esperar. A medida que finaliza cada hilo, llama al método ``await()`` en la barrera cíclica. Una vez que el número especificado de hilos ha llamado ``await()``, la barrera se libera y todos los hilos pueden continuar.\n\nDespués de que se alcanza el límite de CyclicBarrier (lo que quiere decir que la barrera se rompe), todos los hilos se liberan y la cantidad de hilos que esperan en CyclicBarrier vuelve a cero.\n\nEn este punto, CyclicBarrier se puede usar nuevamente para un nuevo conjunto de hilos en espera. Por ejemplo, si nuestro límite de CyclicBarrier es 5 y tenemos 15 hilos que llaman await(), CyclicBarrier se activará un total de tres veces.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falansastre%2Fjava-concurrency","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falansastre%2Fjava-concurrency","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falansastre%2Fjava-concurrency/lists"}