Programación

1º DAM/DAW - Curso 2023-2024

User Tools

Site Tools


apuntes:concurrencia

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
apuntes:concurrencia [2023/05/28 11:02] – [Colecciones sincronizadas] Santiago Faciapuntes:concurrencia [2023/05/28 23:59] (current) Santiago Faci
Line 36: Line 36:
 Es la programación de aplicaciones en las que las tareas a ejecutar se reparten entre varios equipos diferentes (conectados en red, a los que llamaremos nodos). Juntos, estos equipos, forman lo que se conoce como un Sistema Distribuido, que busca formar redes de equipos que trabajen con un fin común  Es la programación de aplicaciones en las que las tareas a ejecutar se reparten entre varios equipos diferentes (conectados en red, a los que llamaremos nodos). Juntos, estos equipos, forman lo que se conoce como un Sistema Distribuido, que busca formar redes de equipos que trabajen con un fin común 
  
-\begin{figure}[h!] +<figure> 
-    \centering +{concurrencia.jpg }
-    \includegraphics[scale=0.8]{partes/varios/concurrencia.jpg} +<caption>Concurrencia</caption></figure>
-    \caption{Programación concurrente/paralela} +
-\end{figure}+
  
-\begin{figure}[h!] +<figure> 
-    \centering +{distribuida.jpg }
-    \includegraphics[scale=1]{partes/varios/distribuida.jpg} +<caption>Programación distribuida</caption></figure>
-    \caption{Programación distribuida+
-\end{figure}+
  
 ==== ¿Qué son los hilos? ==== ==== ¿Qué son los hilos? ====
Line 57: Line 53:
  
 === Estados de un hilo === === Estados de un hilo ===
- +<figure> 
-\begin{figure}[h!] +{estados_hilo.png }
-    \centering +<caption>Estados de un hilo</caption></figure>
-    \includegraphics[scale=1.4]{partes/varios/estados_hilo.png} +
-    \caption{Estados de un hilo+
-\end{figure}+
  
 === Programación multihilo en Java === === Programación multihilo en Java ===
Line 70: Line 63:
 Para la creación de hilos en Java disponemos de varias vías, combinando el uso de la clase Thread y el interface Runnable según nos interese: Para la creación de hilos en Java disponemos de varias vías, combinando el uso de la clase Thread y el interface Runnable según nos interese:
  
-  * Podemos utiliza la clase Thread heredando de ella. Es quizás la forma más cómoda porque una clase que hereda de Thread se convierte automáticamente en un hilo. Tiene una pega: esa clase ya no podrá heredera de ninguna otra, por lo que si la arquitectura de nuestra aplicación lo requiere ya no podríamos. +  * Podemos utiliza la clase ''Thread'' heredando de ella. Es quizás la forma más cómoda porque una clase que hereda de Thread se convierte automáticamente en un hilo. Tiene una pega: esa clase ya no podrá heredera de ninguna otra, por lo que si la arquitectura de nuestra aplicación lo requiere ya no podríamos. 
-  * Si tenemos la limitación que acabamos de comentar para el primer caso, podemos implementar el interface Runnable de forma que la clase que nosotros estamos implementado podrá además heredar sin ninguna limitación. Sólo cambia un poco la forma de trabajar directamente con la clase hilo.+  * Si tenemos la limitación que acabamos de comentar para el primer caso, podemos implementar el interface ''Runnable'' de forma que la clase que nosotros estamos implementado podrá además heredar sin ninguna limitación. Sólo cambia un poco la forma de trabajar directamente con la clase hilo.
   * Por otra parte también podemos crear un hilo utilizando una clase anónima. No es un método que se recomiende pero en algunos casos, cuando la clase que hace de hilo no va a tener una estructura concreta es bastante cómodo hacerlo de esta manera.   * Por otra parte también podemos crear un hilo utilizando una clase anónima. No es un método que se recomiende pero en algunos casos, cuando la clase que hace de hilo no va a tener una estructura concreta es bastante cómodo hacerlo de esta manera.
  
Line 153: Line 146:
 En cualquier caso tenemos que tener siempre en cuenta las siguientes consideraciones: En cualquier caso tenemos que tener siempre en cuenta las siguientes consideraciones:
  
-  * Siempre se debe sobreescribir (Override) el método run() e implementar allí lo que tiene que hacer el hilo+  * Siempre se debe sobreescribir (Override) el método ''run()'' e implementar allí lo que tiene que hacer el hilo
   * Podemos hacer que el hilo haga un número finito de cosas o bien que esté siempre en segundo plano (tendremos que asegurar que el método run() se ejecuta de forma continuada)(¿cómo se hace eso?)   * Podemos hacer que el hilo haga un número finito de cosas o bien que esté siempre en segundo plano (tendremos que asegurar que el método run() se ejecuta de forma continuada)(¿cómo se hace eso?)
   * Los problemas vienen cuando existen varios hilos. Hay que tener en cuenta que pueden compartir datos y código y encontrarse en diferentes estados de ejecución   * Los problemas vienen cuando existen varios hilos. Hay que tener en cuenta que pueden compartir datos y código y encontrarse en diferentes estados de ejecución
Line 159: Line 152:
   * Además, en el caso de aplicaciones multihilo, también nos puede interesar sincronizar y comunicar unos hilos con otros   * Además, en el caso de aplicaciones multihilo, también nos puede interesar sincronizar y comunicar unos hilos con otros
  
-También resulta interesante saber cómo detener un hilo. En este caso, la API de Java desaconsejó el método stop() que en un principio ideó para detener la ejecución. Así, hoy en día, se nos anima a que seamos nosotros quienes implementemos formas limpias de detener nuestros hilos.+También resulta interesante saber cómo detener un hilo. En este caso, la API de Java desaconsejó el método ''stop()'' que en un principio ideó para detener la ejecución. Así, hoy en día, se nos anima a que seamos nosotros quienes implementemos formas limpias de detener nuestros hilos.
  
 ==== Sincronización de hilos ==== ==== Sincronización de hilos ====
  
-El API de Java proporciona una serie de métodos en la clase Thread para la sincronización de los hilos en una aplicación:+El API de Java proporciona una serie de métodos en la clase ''Thread'' para la sincronización de los hilos en una aplicación:
  
-  -  //join()//  Se espera la terminación del hilo que invoca a este método antes de continuar +  - ''join()'' Se espera la terminación del hilo que invoca a este método antes de continuar 
-  - //Thread.sleep(int)//  El hilo que ejecuta esta llamada permanece dormido durante el tiempo especificado como parámetro (en ms) +  - ''Thread.sleep(int)'' El hilo que ejecuta esta llamada permanece dormido durante el tiempo especificado como parámetro (en ms) 
-  - isAlive()//  Comprueba si el hilo permanece activo todavía (no ha terminado su ejecución) +  - ''isAlive()'' Comprueba si el hilo permanece activo todavía (no ha terminado su ejecución) 
-  - yield()//  Sugiere al scheduler que sea otro hilo el que se ejecute (no se asegura)+  - ''yield()'' Sugiere al scheduler que sea otro hilo el que se ejecute (no se asegura)
  
 <code java> <code java>
Line 283: Line 276:
 </code> </code>
  
-//isAlive()//  está indicando que el hilo está vivo (ha iniciado su ejecución y aún no ha muerto, puede estar en cualquier estado intermedio, incluso durmiendo)+''isAlive()'' está indicando que el hilo está vivo (ha iniciado su ejecución y aún no ha muerto, puede estar en cualquier estado intermedio, incluso durmiendo)
  
 ===== Programación concurrente ===== ===== Programación concurrente =====
Line 290: Line 283:
  
   * En un entorno multi-hilo, se da una condición de carrera cuando más de un hilo intenta "al mismo tiempo" actualizar un recurso compartido (que puede ser una variable)   * En un entorno multi-hilo, se da una condición de carrera cuando más de un hilo intenta "al mismo tiempo" actualizar un recurso compartido (que puede ser una variable)
-  * Java proporciona un mecanismo para evitar estos problemas (palabra reserva //synchronized//)+  * Java proporciona un mecanismo para evitar estos problemas (palabra reserva ''synchronized'')
   * Evita que más de un hilo puedan acceder a un zona de código o ejecutar un método determinado   * Evita que más de un hilo puedan acceder a un zona de código o ejecutar un método determinado
  
Line 303: Line 296:
 </code> </code>
  
-Sin la palabra reservada //synchronized//, si más de un hilo ejecuta el método calcular, los resultados obtenidos pueden ser bastante inesperados (hacer prueba con y sin ella)+Sin la palabra reservada ''synchronized'', si más de un hilo ejecuta el método calcular, los resultados obtenidos pueden ser bastante inesperados (hacer prueba con y sin ella) 
 ==== Executors y pools de hilos ==== ==== Executors y pools de hilos ====
  
-En el apartado anterior vimos como crear y lanzar hilos individualmente utilizando la clase //Thread// que Java proporcina en su API. El +En el apartado anterior vimos como crear y lanzar hilos individualmente utilizando la clase ''Thread'' que Java proporcina en su API. El código funciona perfectamente pero realmente se vuelve complicado de desarrollar si lo que tenemos que gestionar es una gran cantidad de hilos de forma simultánea. Para esos casos, Java proporciona un framework, ''Executors'', que permite una gestión mucho más sencillo de
-código funciona perfectamente pero realmente se vuelve complicado de desarrollar si lo que tenemos que gestionar es una gran cantidad de +
-hilos de forma simultánea. Para esos casos, Java proporciona un framework, //Executors//, que permite una gestión mucho más sencillo de+
 cualquier tarea que se ejecuta en segundo plano. cualquier tarea que se ejecuta en segundo plano.
  
-Utilizando //Executors//, como programador, te olvidas en parte de la gestión de los hilos. Simplemente hay que centrarse en programar +Utilizando ''Executors'', como programador, te olvidas en parte de la gestión de los hilos. Simplemente hay que centrarse en programar el código a realizar y asignar dicho código (una clase ''Runnable'' ) a un ''Executor'' , que se encargará de crear el hilo, lanzarlo y gestionar su ejecución.
-el código a realizar y asignar dicho código (una clase //Runnable// ) a un //Executor// , que se encargará de crear el hilo, lanzarlo y +
-gestionar su ejecución.+
  
-Veamos un ejemplo de cómo lanzar una serie de tareas en segundo plano utilizando la clase \verb ExecutorService  que viene con el framework +Veamos un ejemplo de cómo lanzar una serie de tareas en segundo plano utilizando la clase ''ExecutorService''  que viene con el framework ''Executors'' de Java.
-//Executors// de Java.+
  
-Supongamos una clase muy sencilla que implementa \verb Runnable  para definir un código que debe ejecutarse en segundo plano:+Supongamos una clase muy sencilla que implementa ''Runnable'' para definir un código que debe ejecutarse en segundo plano:
  
 <code java> <code java>
Line 330: Line 319:
 </code> </code>
  
-Para lanzar la tarea, o varias instancia de la misma, podemos crear un pool de hilos (en este caso dos) y pasarles las tareas al objeto  +Para lanzar la tarea, o varias instancia de la misma, podemos crear un pool de hilos (en este caso dos) y pasarles las tareas al objeto ''Executor'' para que sea él quién los lance. En este caso, puesto que sólo se dispone de dos hilos en el pool, sólo se podrán ejecutar dos hilos en cada momento. Si en algún momento se ocuparán los dos hilos, las demás tareas tendrían que esperar hasta que alguno de los dos quedará libre para ejecutarse.
-//Executor//  para que sea él quién los lance. En este caso, puesto que sólo se dispone de dos hilos en el pool, sólo se podrán ejecutar +
-dos hilos en cada momento. Si en algún momento se ocuparán los dos hilos, las demás tareas tendrían que esperar hasta que alguno de los dos +
-quedará libre para ejecutarse.+
  
-Finalmente se ejecuta el método //shutdown()//, que hará finalizar el //Executor// una vez hayan terminado todas las tareas que se +Finalmente se ejecuta el método ''shutdown()'', que hará finalizar el ''Executor'' una vez hayan terminado todas las tareas que se encuentran ejecutándose con él.
-encuentran ejecutándose con él.+
  
-Hay que tener en cuenta que existe también un método //shutdownNow//  que hace que el //Executor// finalice devolviendo todas las +Hay que tener en cuenta que existe también un método ''shutdownNow'' que hace que el ''Executor'' finalice devolviendo todas las tareas que estaban esperando y no llegaron a ejecutarse. Se esperará que las tareas activas terminen o bien sean interrumpidas.
-tareas que estaban esperando y no llegaron a ejecutarse. Se esperará que las tareas activas terminen o bien sean interrumpidas.+
  
 <code java> <code java>
Line 356: Line 340:
 </code> </code>
  
-También es posible trabajar con un //ExecutorService// que simplemente cuente con un hilo+También es posible trabajar con un ''ExecutorService'' que simplemente cuente con un hilo
  
 <code java> <code java>
Line 386: Line 370:
 En cualquier caso, podemos encontrarnos con dos tipos de pools de hilos: En cualquier caso, podemos encontrarnos con dos tipos de pools de hilos:
  
-  * **Fixed**: Se pueden crear a través de los métodos \verb Executors.newFixedThreadPool(...)  y crean un pool de hilos fijo (de número determinado) que se reutilizan a medida que van quedando libres. En ningún caso se crearán más hilos de los fijados al crear el pool. Si todos los hilos están ocupados y se encola una nueva tarea, ésta tendrá que esperar hasta que un hilo quede libre para ejecutarla. +  * **Fixed**: Se pueden crear a través de los métodos ''Executors.newFixedThreadPool(...)'' y crean un pool de hilos fijo (de número determinado) que se reutilizan a medida que van quedando libres. En ningún caso se crearán más hilos de los fijados al crear el pool. Si todos los hilos están ocupados y se encola una nueva tarea, ésta tendrá que esperar hasta que un hilo quede libre para ejecutarla. 
-  * **Cached**: Se pueden crear a través de los métodos \verb Executors.newCachedThreadPool(...)  y crear un pool de hilos no fijo (sin número determinado) que se pueden reutilizar si quedan libres. En este caso, si todos los hilos están ocupados y se encola una nueva tarea se creará un nuevo hilo para poder ejecutarla. Los hilos que no hayan sido reutilizados en un tiempo determinado (6 segundos) serán finalizados y eliminados del pool.+  * **Cached**: Se pueden crear a través de los métodos ''Executors.newCachedThreadPool(...)'' y crear un pool de hilos no fijo (sin número determinado) que se pueden reutilizar si quedan libres. En este caso, si todos los hilos están ocupados y se encola una nueva tarea se creará un nuevo hilo para poder ejecutarla. Los hilos que no hayan sido reutilizados en un tiempo determinado (6 segundos) serán finalizados y eliminados del pool.
  
 ==== Callable y Future ==== ==== Callable y Future ====
  
-Hasta el momento hemos trabajado con objetos //Runnable// como objetos que se pueden asignar a un hilo para ejecutar su código en segundo plano. Los objetos \verb Runnable  no son capaces de devolver ningún valor, simplemente ejecutan el código (el método //run()// no devuelve nada). Tampoco pueden lanzar excepciones puesto que el método //run()// de //Runnable// no está prearado para ello. +Hasta el momento hemos trabajado con objetos ''Runnable'' como objetos que se pueden asignar a un hilo para ejecutar su código en segundo plano. Los objetos ''Runnable'' no son capaces de devolver ningún valor, simplemente ejecutan el código (el método ''run()'' no devuelve nada). Tampoco pueden lanzar excepciones puesto que el método ''run()'' de ''Runnable'' no está prearado para ello. 
-Java proporciona también una interface llamada //Callable// que permite crear objetos que pueden ser asignados a hilos para ejecutarse en segundo plano, y que además permite que éstas tareas devuelvan un resultado al finalizar y que lancen una excepción.+Java proporciona también una interface llamada ''Callable'' que permite crear objetos que pueden ser asignados a hilos para ejecutarse en segundo plano, y que además permite que éstas tareas devuelvan un resultado al finalizar y que lancen una excepción.
  
 <code java> <code java>
Line 404: Line 388:
 </code> </code>
  
-Ligado a este nuevo tipo de tarea //Callable// aparece un nuevo tipo llamado \verb Future  que es el tipo de dato en el que estas nuevas tareas devolverán su valor. Puesto que ahora tenemos una tarea en segundo plano que debe devolver un valor al finalizar, necesitamos, de alguna manera, esperar y recoger ese valor cuando corresponda. Así, un objeto \verb Future  es capaz de almacenar el valor de devolución de un método que ha sido lanzado pero todavía no ha terminado.+Ligado a este nuevo tipo de tarea ''Callable'' aparece un nuevo tipo llamado ''Future'' que es el tipo de dato en el que estas nuevas tareas devolverán su valor. Puesto que ahora tenemos una tarea en segundo plano que debe devolver un valor al finalizar, necesitamos, de alguna manera, esperar y recoger ese valor cuando corresponda. Así, un objeto ''Future'' es capaz de almacenar el valor de devolución de un método que ha sido lanzado pero todavía no ha terminado.
  
-Veamos ahora un ejemplo lanzando una tarea //Callable// y cómo se recoge su resultado:+Veamos ahora un ejemplo lanzando una tarea ''Callable'' y cómo se recoge su resultado:
  
 <code java> <code java>
Line 429: Line 413:
 </code> </code>
  
-Hay que tener en cuenta que la llamada al método //get()// bloquea la ejecución del hilo principal hasta que la tarea termine y devuelva el resultado. La clave aqui está en que unas líneas antes ya tenemos el objeto \verb Future  y podemos seguir ejecutando instrucciones hasta que realmente necesitemos utilizar el valor, que será cuando invoquemos al método //get()//+Hay que tener en cuenta que la llamada al método ''get()'' bloquea la ejecución del hilo principal hasta que la tarea termine y devuelva el resultado. La clave aqui está en que unas líneas antes ya tenemos el objeto ''Future'' y podemos seguir ejecutando instrucciones hasta que realmente necesitemos utilizar el valor, que será cuando invoquemos al método ''get()''. 
-Además, siempre podremos cancelar el objeto //Future// e incluso comprobar si ha terminado para decidir hacer algo mientras la tarea no lo haya hecho. De esa manera, una vez lanzada la tarea y obtenido el futuro, podemos ver si procede o no acabar obteniendo el valor que devuelva. En el siguiente caso, vamos a suponer que a partir de un cierto tiempo de ejecución se cancela la tarea y el programa sigue. Modificamos la tarea //TareaCallable// para forzar a que dure un tiempo determinado y que asi el ejemplo tenga cierta duración y podamos comprobar como espera (o no) en función del tiempo de //timeout// que le hayamos asignado, o bien utilizando otro criterio cualquiera.+ 
 +Además, siempre podremos cancelar el objeto ''Future'' e incluso comprobar si ha terminado para decidir hacer algo mientras la tarea no lo haya hecho. De esa manera, una vez lanzada la tarea y obtenido el futuro, podemos ver si procede o no acabar obteniendo el valor que devuelva. En el siguiente caso, vamos a suponer que a partir de un cierto tiempo de ejecución se cancela la tarea y el programa sigue. Modificamos la tarea ''TareaCallable'' para forzar a que dure un tiempo determinado y que asi el ejemplo tenga cierta duración y podamos comprobar como espera (o no) en función del tiempo de //timeout// que le hayamos asignado, o bien utilizando otro criterio cualquiera.
  
 <code java> <code java>
Line 494: Line 479:
 </code> </code>
  
-En cualquier caso, el método //get()// puede ser invocado para establecer un tiempo de //timeout//, a partir del cual el método devolvería una excepción //TimeoutException//. Es un caso muy útil cuando, desde nuestra aplicación, invocamos a algún servicio remoto del que no podemos garantizar su disponibilidad en todo momento.+En cualquier caso, el método ''get()'' puede ser invocado para establecer un tiempo de //timeout//, a partir del cual el método devolvería una excepción ''TimeoutException''. Es un caso muy útil cuando, desde nuestra aplicación, invocamos a algún servicio remoto del que no podemos garantizar su disponibilidad en todo momento.
  
 <code java> <code java>
Line 503: Line 488:
 </code> </code>
  
-Además, es posible lanzar varias tareas al mismo tiempo con el método //executeAll// de la clase //ExecutorService//:+Además, es posible lanzar varias tareas al mismo tiempo con el método ''executeAll'' de la clase ''ExecutorService'':
  
 <code java> <code java>
Line 515: Line 500:
 . . . . . .
 </code> </code>
 +
 +==== CompletableFuture ====
 +
 +Proporciona una API más completa que la que hay para la clase ''Future''.
 +
 +  * Lanzar una tarea en segundo plano de forma asíncrona y ejecutar un método al finalizar ésta (a través de la llamada a un método callback)
 +
 +<code java>
 +CompletableFuture.runAsync(() -> doSomething())
 +    .whenComplete((string, throwable) -> doSomethingWhenFinishFuture());
 +</code>
 +
 +  * Lanzar una tarea y ejecutar algo cuando ésta termina:
 +
 +<code java>
 +private String doSomethingAndReturnResult() {
 +    return "ok";
 +}
 +
 +CompletableFuture.supplyAsync(() -> doSomethingAndReturnResult())
 +                .thenAccept(System.out::println)
 +                .whenComplete((nothing, error) -> System.out.println("Fin"));
 +</code>
 +
  
 ==== CountDownLatch ==== ==== CountDownLatch ====
Line 549: Line 558:
 ==== CyclicBarrier ==== ==== CyclicBarrier ====
  
-Similar a CountDownLatch pero reutilizable+Similar a ''CountDownLatch'' pero reutilizable
  
 Se utiliza para que los hilos se esperen los unos a los otros antes de realizar alguna tarea concreta Se utiliza para que los hilos se esperen los unos a los otros antes de realizar alguna tarea concreta
Line 578: Line 587:
 } }
 </code> </code>
 +
 ==== Colecciones sincronizadas ==== ==== Colecciones sincronizadas ====
  
-Son lo que se conocen como colecciones thread-safe, puesto que están listas para usarse en entornos de concurrencia con varios hilos ejecutándose.+Son lo que se conocen como colecciones //thread-safe//, puesto que están listas para usarse en entornos de concurrencia con varios hilos ejecutándose. 
 + 
 +En la clase ''Collection'' hay una serie de métodos estáticos que devuelven instancia de colecciones sincronizadas. Por ejemplo:
  
-En la clase Collection hay una serie de métodos estáticos que devuelven instancia de colecciones sincronizadas. Por ejemplo: 
   * synchronizedList()   * synchronizedList()
   * synchronizedMap()   * synchronizedMap()
   * synchronizedSet()   * synchronizedSet()
  
-Además, existe alguna colección ya sincronizada como //Vector// (similar a //ArrayList//) //HashTable// (similar a //HashMap//)+Además, existe alguna colección ya sincronizada como ''Vector'' (similar a ''ArrayList'' ''HashTable'' (similar a ''HashMap'')
  
 ==== Objetos atómicos ==== ==== Objetos atómicos ====
  
-==== Objetos volatile ====+Los objetos atómicos son estructuras Java que sirven para representar valores y realizar operaciones de forma atómica para algunos tipos de datos. 
 + 
 +''AtomicInt'', ''AtomicLong// y ''AtomicBoolean'' son algunos de los objetos atómicos que tenemos disponibles dentro de la API de Java 
 + 
 +Por poner algún ejemplo, ''AtomicInt'' garantiza la ejecución atómica de algunas operaciones como estas: 
 +  * ''get() 
 +  * ''compareAndSet(valorComparacion, nuevoValor)'' 
 +  * ''addAndGet(valor)'' 
 +  * ''getAndIncrement()'' 
 +  * ''incrementAndGet()'' 
 +  * ''decrementAndGet()'' 
 +  * ''getAndDecrement()'' 
 + 
 + 
 +==== Variables volatile ==== 
 + 
 +Es una palabra reservada que sirve para indicarle a Java que la variable debe ser escriba en memoria principal de forma inmediata cuando vea modificado su valor, para evitar que otros hilos que la utilicen puedan tener un valor diferente no actualizado 
 + 
 +  * private volatile int valor; 
 + 
 +Hay que tener en cuenta que no siempre será suficiente y sólo servirá para situaciones donde un hilo esté leyendo alguna variable que otro hilo modifique. No nos ayudará a evitar las condiciones de carrera que hemos visto anteriormente. En esos casos necesitaremos, como ya vimos, crear zonas sincronizadas para evitar problemas. 
 + 
 +<figure> 
 +{{ volatile.png }} 
 +<caption>variable volatile</caption></figure> 
  
 ==== Streams paralelos ==== ==== Streams paralelos ====
 +
 +La ejecución en paralelo significa que se ejecutan fragmentos de código de una aplicación al mismo tiempo.
 +
 +Hay que tener en cuenta que, en determinadas circunstancias, puede no interesar puesto que, al ejecutarse de forma paralela, no se garantiza en que orden se ejecutarán
 +
 +En otras circustancias puede interesar puesto que la aplicación aprovecha mucho más los recursos de la máquina al forzar a ésta a ejecutar algunas operaciones de forma simultánea
 +
 +<figure>
 +{{ parallel-sequential.png }}
 +<caption>Ejecución stream secuencial vs stream paralelo</caption></figure>
 +
 +En los streams paralelos, se procesan los elementos de forma paralela por segmentos
 +
 +<code java>
 +List<String> cities = new ArrayList<>(); 
 +// TODO Poblar la lista de ciudades 
 +cities.parallelStream()
 +  .forEach(System.out::println);
 +</code> 
 +
 +Puede mejorar el rendimiento en algunos casos, ya que permite que la aplicación realice algunos cálculos (matemáticos en este caso) de forma paralela, aprovechando mucho más los recursos de la máquina
 +
 +<code java>
 +long count = Stream.iterate(0, i -> i + 1)
 +  .limit(500000)
 +  .parallel() 
 +  .filter(Main::isPrime) 
 +  .count();
 +System.out.println("Hay " + count + " números primos");
 +</code>
  
 ==== Locks ==== ==== Locks ====
  
 +Es otro mecanismo para la sincronización de hilos parecido a como hace ''synchronized''
 +
 +Es algo más flexible que ''synchronized'', puesto que podemos bloquear/desbloquear la zona exclusiva en diferentes métodos 
 +
 +''Lock'' es un interface que tiene varias opciones para implementar. Veremos un ejemplo con ''ReentrantLock''
 +
 +<code java>
 +Lock lock = new ReentrantLock(); 
 +lock.lock();
 +try {
 +  // Aqui se accede al recurso/código compartido
 +} finally { 
 +  lock.unlock();
 +}
 +</code>
 +
 +Podemos encapsular la operación de bloqueo (lock) para evitar que se produzcan deadlocks (esperar indefinidamente un recurso) utilizando el método ''tryLock'' indicando el tiempo que el hilo esperará por el recurso
 +
 +<code java>
 +boolean isLocked = lock.tryLock(2, TimeUnit.SECONDS); 
 +if (isLocked) {
 +  try {
 +  // Aqui se accede al recurso/código compartido
 +  } finally { 
 +    lock.unlock();
 +  } 
 +}
 +</code>
 ==== Patrón Observer ==== ==== Patrón Observer ====
 +
 +Patrón de diseño por el cual un objeto, llamada sujeto, mantiene a otros objetos, llamados observadores, notificados acerca de cualquier cambio que sobre él se produce.
 +
 +Hasta Java 9, se proporcionaba una serie de clases que permitían definir una solución para este Patrón. A partir de entonces se recomienda el uso de ''PropertyChangeListener'' para dicha implementación.
 +
 +=== Clase Observable ===
 +
 +<code java>
 +public class Product {
 +  private String nombre;
 +  . . .
 +  private PropertyChangeSupport change;
 +  
 +  @Override
 +  public void addPropertyChangeListener(PropertyChangeListener listener) {
 +    change.addPropertyChangeListener(listener); 
 +  }
 +  
 +  @Override
 +  public void removePropertyChangeListener(PropertyChangeListener listener) {
 +    change.removePropertyChangeListener(listener); 
 +  }
 +  
 +  public void decreaseStock(int quantity) { 
 +    change.firePropertyChange("stock", stock, (stock – quantity));
 +    stock -= quantity;
 +  } 
 +}
 +</code>
 +
 +=== La clase Observer ===
 +
 +<code java>
 +public class Provider implements PropertyChangeListener { 
 +  private String name;
 +  . . .
 +  @Override
 +  public void propertyChange(PropertyChangeEvent event) {
 +    if (event.getPropertyName().equals("stock")) { 
 +      System.out.println("El stock ha bajado");
 +    } 
 +  }
 +}
 +</code>
 +
 +Y el programa principal para ver el ejemplo funcionar:
 +
 +<code java>
 +public static void main(String args[]) { 
 +  Product product = new Product();
 +  . . .
 +  Customer customer = new Customer();
 +  . . . 
 +  product.addPropertyChangeListener(customer); 
 +  product.setStock(10); // ¿Qué ocurre aqui? .. .
 +}
 +</code>
  
 ---- ----
apuntes/concurrencia.1685271766.txt.gz · Last modified: 2023/05/28 11:02 by Santiago Faci