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
Last revisionBoth sides next revision
apuntes:concurrencia [2023/05/28 10:59] – [CountDownLatch] Santiago Faciapuntes:concurrencia [2023/05/28 11:44] – [Streams paralelos] 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 515: Line 508:
 . . . . . .
 </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 526: Line 543:
   private CountDownLatch countDownLatch;   private CountDownLatch countDownLatch;
      
-  public Tarea(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch;+  public Tarea(CountDownLatch countDownLatch) {  
 +    this.countDownLatch = countDownLatch;
   }   }
  
Line 539: Line 557:
 . . . . . .
  
-CountDownLatch countDownLatch = new CountDownLatch(4); for . . . +CountDownLatch countDownLatch = new CountDownLatch(4);  
-executorService.execute(new Tarea(countDownLatch)); ...+for . . . 
 +  executorService.execute(new Tarea(countDownLatch)); 
 +. . .
 countDownLatch.await(); // Espera hasta que el contador sea 0 System.out.println("Ya se han ejecutado las 5 tareas"); countDownLatch.await(); // Espera hasta que el contador sea 0 System.out.println("Ya se han ejecutado las 5 tareas");
 </code> </code>
Line 546: Line 566:
 ==== CyclicBarrier ==== ==== CyclicBarrier ====
  
 +Similar a CountDownLatch pero reutilizable
 +
 +Se utiliza para que los hilos se esperen los unos a los otros antes de realizar alguna tarea concreta
 +
 +<code java>
 +public class Tarea implements Runnable {
 +  private CyclicBarrier cyclicBarrier;
 +  
 +  public Tarea(CyclicBarrier cyclicBarrier) { 
 +    this.cyclicBarrier = cyclicBarrier;
 +  }
 +
 +  @Override
 +  public void run() {
 +    hacerAlgo(); 
 +    cyclicBarrier.await(); 
 +    hacerOtraCosa();
 +  } 
 +}
 +
 +. . . 
 +
 +// Acepta como segundo argumento un Runnable que será ejecutado por el último hilo que pase la barrera
 +CyclicBarrier cyclicBarrier = new CyclicBarrier(2); 
 +if (!cyclicBarrier.isBroken()) {
 +  executorService.execute(new Tarea(cyclicBarrier)); 
 +  . . .
 +}
 +</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.
 +
 +En la clase Collection hay una serie de métodos estáticos que devuelven instancia de colecciones sincronizadas. Por ejemplo:
 +  * synchronizedList()
 +  * synchronizedMap()
 +  * synchronizedSet()
 +
 +Además, existe alguna colección ya sincronizada como //Vector// (similar a //ArrayList//) y //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.txt · Last modified: 2023/05/28 23:59 by Santiago Faci