apuntes:concurrencia
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
apuntes:concurrencia [2021/03/10 13:18] – created Santiago Faci | apuntes:concurrencia [2023/05/28 23:59] (current) – Santiago Faci | ||
---|---|---|---|
Line 3: | Line 3: | ||
===== Programación multihilo ====== | ===== Programación multihilo ====== | ||
- | \part*{Programación concurrente} | + | ==== Multiproceso |
- | + | ||
- | \section{Introducción a la programación multihilo} | + | |
- | + | ||
- | \subsection{Multiproceso} | + | |
El multiproceso consiste en la ejecución de varios procesos diferentes de forma simultánea para la realización de una o varias tareas relacionadas o no entre sí. En este caso, cada uno de estos procesos es una aplicación independiente. El caso más conocido es aquel en el que nos referimos al Sistema Operativo (Windows, Linux, MacOS, . . .) y decimos que es multitarea puesto que es capaz de ejecutar varias tareas o procesos (o programas) al mismo tiempo. | El multiproceso consiste en la ejecución de varios procesos diferentes de forma simultánea para la realización de una o varias tareas relacionadas o no entre sí. En este caso, cada uno de estos procesos es una aplicación independiente. El caso más conocido es aquel en el que nos referimos al Sistema Operativo (Windows, Linux, MacOS, . . .) y decimos que es multitarea puesto que es capaz de ejecutar varias tareas o procesos (o programas) al mismo tiempo. | ||
- | \subsection{Multihilo} | + | ==== Multihilo |
Hablamos de multihilo cuando se ejecutan varias tareas relacionadas o no entre sí dentro de una misma aplicación. En este caso no son procesos diferentes sino que dichas tareas se ejecutan dentro del mismo proceso del Sistema Operativo. A cada una de estas tareas se le conoce como hilo o thread (en algunos contextos también como procesos ligeros). | Hablamos de multihilo cuando se ejecutan varias tareas relacionadas o no entre sí dentro de una misma aplicación. En este caso no son procesos diferentes sino que dichas tareas se ejecutan dentro del mismo proceso del Sistema Operativo. A cada una de estas tareas se le conoce como hilo o thread (en algunos contextos también como procesos ligeros). | ||
Line 17: | Line 13: | ||
En ambos casos estaríamos hablando de lo que se conoce como Programación Concurrente. Hay que tener en cuenta que en ninguno de los dos casos la ejecución es realmente simultánea, | En ambos casos estaríamos hablando de lo que se conoce como Programación Concurrente. Hay que tener en cuenta que en ninguno de los dos casos la ejecución es realmente simultánea, | ||
- | \subsection{Algoritmos de planificación} | + | ==== Algoritmos de planificación |
En entornos multitarea, un algoritmo de planificación indica la forma en que el tiempo de procesamiento debe repartirse entre todas las tareas que deben ejecutarse en un momento determinado. Existen diferentes algoritmos de planificación, | En entornos multitarea, un algoritmo de planificación indica la forma en que el tiempo de procesamiento debe repartirse entre todas las tareas que deben ejecutarse en un momento determinado. Existen diferentes algoritmos de planificación, | ||
- | \begin{itemize} | + | * Debe ser imparcial y eficiente |
- | \item Debe ser imparcial y eficiente | + | |
- | | + | |
- | | + | |
- | | + | |
- | \end{itemize} | + | |
- | \subsection{Programación concurrente, | + | ==== Programación concurrente, |
- | \subsubsection*{Programación concurrente} | + | === Programación concurrente |
Es la programación de aplicaciones capaces de realizar varias tareas de forma simultánea utilizando hilos o threads. En este caso todas las tareas compiten por el uso del procesador (lo más habitual es disponer sólo de uno) y en un instante determinado sólo una de ellas se encuentra en ejecución. Además, habrá que tener en cuenta que diferentes hilos pueden compartir información entre sí y eso complica mucho su programación y coordinación. | Es la programación de aplicaciones capaces de realizar varias tareas de forma simultánea utilizando hilos o threads. En este caso todas las tareas compiten por el uso del procesador (lo más habitual es disponer sólo de uno) y en un instante determinado sólo una de ellas se encuentra en ejecución. Además, habrá que tener en cuenta que diferentes hilos pueden compartir información entre sí y eso complica mucho su programación y coordinación. | ||
- | \subsubsection*{Programación paralela} | + | === Programación paralela |
Es la programación de aplicaciones que ejecutan tareas de forma paralela, de forma que no compiten por el procesador puesto que cada una de ellas se ejecuta en uno diferente. Normalmente buscan resultados comunes dividiendo el problema en varias tareas que se ejecutan al mismo tiempo. | Es la programación de aplicaciones que ejecutan tareas de forma paralela, de forma que no compiten por el procesador puesto que cada una de ellas se ejecuta en uno diferente. Normalmente buscan resultados comunes dividiendo el problema en varias tareas que se ejecutan al mismo tiempo. | ||
- | \subsubsection*{Programación distribuida} | + | === Programación distribuida |
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, | 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, | ||
- | \begin{figure}[h!] | + | <figure> |
- | | + | {{ concurrencia.jpg |
- | \includegraphics[scale=0.8]{partes/ | + | <caption> |
- | \caption{Programación concurrente/paralela} | + | |
- | \end{figure} | + | |
- | \begin{figure}[h!] | + | <figure> |
- | \centering | + | {{ distribuida.jpg |
- | | + | <caption>Programación distribuida</ |
- | \caption{Programación distribuida} | + | |
- | \end{figure} | + | |
- | \subsection{¿Qué son los hilos?} | + | ==== ¿Qué son los hilos? |
Un hilo o thread es cada una de las tareas que puede realizar de forma simultánea una aplicación. Por defecto, toda aplicación dispone de un único hilo de ejecución, al que se conoce como hilo principal. Si dicha aplicación no despliega ningún otro hilo, sólo será capaz de ejecutar una tarea al mismo tiempo en ese hilo principal. | Un hilo o thread es cada una de las tareas que puede realizar de forma simultánea una aplicación. Por defecto, toda aplicación dispone de un único hilo de ejecución, al que se conoce como hilo principal. Si dicha aplicación no despliega ningún otro hilo, sólo será capaz de ejecutar una tarea al mismo tiempo en ese hilo principal. | ||
Line 62: | Line 52: | ||
En cualquier caso, es muy importante conocer los estados en los que se pueden encontrar un hilo. Estos estados se suelen representar mediante un gráfico como el que sigue: | En cualquier caso, es muy importante conocer los estados en los que se pueden encontrar un hilo. Estos estados se suelen representar mediante un gráfico como el que sigue: | ||
- | \subsubsection*{Estados de un hilo} | + | === Estados de un hilo === |
+ | < | ||
+ | {{ estados_hilo.png | ||
+ | < | ||
- | \begin{figure}[h!] | + | === Programación multihilo en Java === |
- | \centering | + | |
- | \includegraphics[scale=1.4]{partes/ | + | |
- | \caption{Estados de un hilo} | + | |
- | \end{figure} | + | |
- | \section{Programación multihilo en Java} | + | ==== Creación de un hilo ==== |
- | + | ||
- | \subsection{Creación de un hilo} | + | |
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: | ||
- | \begin{itemize} | + | * Podemos utiliza la clase '' |
- | \item 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. | + | |
- | | + | |
- | | + | |
- | \end{itemize} | + | |
- | \subsubsection*{Crear un hilo heredando de la clase Thread} | + | === Crear un hilo heredando de la clase Thread |
- | \begin{lstlisting}[language=java] | + | < |
public class Tarea extends Thread { | public class Tarea extends Thread { | ||
@Override | @Override | ||
Line 104: | Line 89: | ||
} | } | ||
} | } | ||
- | \end{lstlisting} | + | </ |
- | \subsubsection*{Crear un hilo implementando la interfaz Runnable} | + | === Crear un hilo implementando la interfaz Runnable |
- | \begin{lstlisting}[language=java] | + | < |
public class OtraClase { | public class OtraClase { | ||
. . . | . . . | ||
Line 136: | Line 121: | ||
} | } | ||
} | } | ||
- | \end{lstlisting} | + | </ |
- | \subsubsection*{Crear un hilo implementando una clase anónima/ | + | === Crear un hilo implementando una clase anónima/ |
- | \begin{lstlisting}[language=java] | + | < |
public class Programa { | public class Programa { | ||
public static void main(String args[]) { | public static void main(String args[]) { | ||
Line 157: | Line 142: | ||
} | } | ||
} | } | ||
- | \end{lstlisting} | + | </ |
En cualquier caso tenemos que tener siempre en cuenta las siguientes consideraciones: | En cualquier caso tenemos que tener siempre en cuenta las siguientes consideraciones: | ||
- | \begin{itemize} | + | * Siempre se debe sobreescribir (Override) el método |
- | \item Siempre se debe sobreescribir (Override) el método run() e implementar allí lo que tiene que hacer el hilo | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | \end{itemize} | + | |
- | 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 |
- | \subsection{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 '' |
- | \begin{itemize} | + | - '' |
- | \item \verb join() | + | - '' |
- | \item \verb Thread.sleep(int) | + | - '' |
- | \item \verb isAlive() | + | - '' |
- | \item \verb yield() | + | |
- | \end{itemize} | + | |
- | \begin{lstlisting}[language=java] | + | < |
public static void main(String args[]) { | public static void main(String args[]) { | ||
Hilo hilo1 = new Thread(new Tarea()); | Hilo hilo1 = new Thread(new Tarea()); | ||
Line 198: | Line 179: | ||
System.out.println(" | System.out.println(" | ||
} | } | ||
- | \end{lstlisting} | + | </ |
El hilo principal espera a que ambos hilos se hayan ejecutado para continuar (o para lo que sea) | El hilo principal espera a que ambos hilos se hayan ejecutado para continuar (o para lo que sea) | ||
- | \begin{lstlisting}[language=java] | + | < |
public static void main(String args[]) { | public static void main(String args[]) { | ||
Hilo hilo1 = new Thread(new Tarea()); | Hilo hilo1 = new Thread(new Tarea()); | ||
Line 215: | Line 196: | ||
System.out.println(" | System.out.println(" | ||
} | } | ||
- | \end{lstlisting} | + | </ |
En este caso los hilos se ejecutan uno después de otro | En este caso los hilos se ejecutan uno después de otro | ||
- | \begin{lstlisting}[language=java] | + | < |
public class Tarea implements Runnable { | public class Tarea implements Runnable { | ||
@Override | @Override | ||
Line 233: | Line 214: | ||
} | } | ||
} | } | ||
- | \end{lstlisting} | + | </ |
En este caso el hilo duerme (detiene su ejecución) durante el tiempo especificado (en ms). Durante ese momento podrán ejecutarse otros hilos | En este caso el hilo duerme (detiene su ejecución) durante el tiempo especificado (en ms). Durante ese momento podrán ejecutarse otros hilos | ||
- | \begin{lstlisting}[language=java] | + | < |
public class TareaPrincipal implements Runnable { | public class TareaPrincipal implements Runnable { | ||
Line 293: | Line 274: | ||
} | } | ||
} | } | ||
- | \end{lstlisting} | + | </ |
+ | |||
+ | '' | ||
+ | |||
+ | ===== Programación concurrente ===== | ||
+ | |||
+ | ==== synchronize ==== | ||
+ | |||
+ | * En un entorno multi-hilo, se da una condición de carrera cuando más de un hilo intenta "al mismo tiempo" | ||
+ | * Java proporciona un mecanismo para evitar estos problemas (palabra reserva '' | ||
+ | * Evita que más de un hilo puedan acceder a un zona de código o ejecutar un método determinado | ||
+ | |||
+ | <code java> | ||
+ | public class RecursoCompartido | ||
+ | private int valor; | ||
+ | |||
+ | public synchronized void calcular() { | ||
+ | setValor(getValor() + 1); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Sin la palabra reservada '' | ||
+ | |||
+ | ==== Executors y pools de hilos ==== | ||
+ | |||
+ | En el apartado anterior vimos como crear y lanzar hilos individualmente utilizando la clase '' | ||
+ | cualquier tarea que se ejecuta en segundo plano. | ||
+ | |||
+ | Utilizando '' | ||
+ | |||
+ | Veamos un ejemplo de cómo lanzar una serie de tareas en segundo plano utilizando la clase '' | ||
+ | |||
+ | Supongamos una clase muy sencilla que implementa '' | ||
+ | |||
+ | <code java> | ||
+ | public class Tarea implements Runnable { | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println(" | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | Finalmente se ejecuta el método '' | ||
+ | |||
+ | Hay que tener en cuenta que existe también un método '' | ||
+ | |||
+ | <code java> | ||
+ | // Crea un executor con dos hilos | ||
+ | ExecutorService executor = Executors.newFixedThreadPool(2); | ||
+ | |||
+ | // Encola tareas a ajecutar | ||
+ | executor.execute(new Tarea()); | ||
+ | executor.execute(new Tarea()); | ||
+ | executor.execute(new Tarea()); | ||
+ | executor.execute(new Tarea()); | ||
+ | |||
+ | // Finaliza el executor. Terminará cuando todas las tareas que tiene | ||
+ | // se acaben de forma natural | ||
+ | executor.shutdown(); | ||
+ | </ | ||
+ | |||
+ | También es posible trabajar con un '' | ||
+ | |||
+ | <code java> | ||
+ | // Crea un executor con dos hilos | ||
+ | ExecutorService executor = Executors.newSingleThreadExecutor(); | ||
+ | executor.execute(new Tarea()); | ||
+ | . . . | ||
+ | </ | ||
+ | |||
+ | En el siguiente caso, instanciamos un pool de hilos planificados, | ||
+ | retraso asignado en su lanzamiento a través del método // | ||
+ | |||
+ | <code java> | ||
+ | // Crea un executor planificado con dos hilos | ||
+ | ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); | ||
+ | |||
+ | // Encola tareas a ajecutar añadiendo retraso | ||
+ | // en el lanzamiento de cada una de ellas | ||
+ | executor.schedule(new Tarea(), 1, TimeUnit.SECONDS); | ||
+ | executor.schedule(new Tarea(), 2, TimeUnit.SECONDS); | ||
+ | executor.schedule(new Tarea(), 3, TimeUnit.SECONDS); | ||
+ | executor.schedule(new Tarea(), 4, TimeUnit.SECONDS); | ||
+ | |||
+ | // Finaliza el executor. Terminará cuando todas las tareas que tiene | ||
+ | // se acaben de forma natural | ||
+ | executor.shutdown(); | ||
+ | </ | ||
+ | |||
+ | En cualquier caso, podemos encontrarnos con dos tipos de pools de hilos: | ||
+ | |||
+ | * **Fixed**: Se pueden crear a través de los métodos '' | ||
+ | * **Cached**: Se pueden crear a través de los métodos '' | ||
+ | |||
+ | ==== Callable y Future ==== | ||
+ | |||
+ | Hasta el momento hemos trabajado con objetos '' | ||
+ | Java proporciona también una interface llamada '' | ||
+ | |||
+ | <code java> | ||
+ | public class TareaCallable implements Callable< | ||
+ | @Override | ||
+ | public Boolean call() throws Exception { | ||
+ | // Realiza algo | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Ligado a este nuevo tipo de tarea '' | ||
+ | |||
+ | Veamos ahora un ejemplo lanzando una tarea '' | ||
+ | |||
+ | <code java> | ||
+ | // Crea un executor con dos hilos | ||
+ | ExecutorService executor = Executors.newFixedThreadPool(2); | ||
+ | |||
+ | // Los Callable se ejecutan con el método submit() | ||
+ | Future< | ||
+ | |||
+ | // Podemos seguir ejecutando código en el hilo principal | ||
+ | System.out.println(" | ||
+ | |||
+ | // Recogemos el resultado de la tarea Callable | ||
+ | // Hay que tener en cuenta que la llamada al método get() | ||
+ | // bloquea la ejecución hasta que termine la tarea | ||
+ | Boolean resultado = futuro.get(); | ||
+ | System.out.println(" | ||
+ | |||
+ | // Finaliza el executor. Terminará cuando todas las tareas que tiene | ||
+ | // se acaben de forma natural | ||
+ | executor.shutdown(); | ||
+ | </ | ||
+ | |||
+ | Hay que tener en cuenta que la llamada al método '' | ||
+ | |||
+ | Además, siempre podremos cancelar el objeto '' | ||
+ | |||
+ | <code java> | ||
+ | public class TareaCallable implements Callable< | ||
+ | @Override | ||
+ | public Boolean call() throws Exception { | ||
+ | // Realiza algo | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y el programa principal quedaría asi: | ||
+ | |||
+ | <code java> | ||
+ | // Crea un executor con dos hilos | ||
+ | ExecutorService executor = Executors.newSingleThreadExecutor(); | ||
+ | |||
+ | long tiempoPasado = 0; | ||
+ | long tiempoInicio = System.currentTimeMillis(); | ||
+ | // Los Callable se ejecutan con el método submit() | ||
+ | Future< | ||
+ | |||
+ | // Podemos seguir ejecutando código en el hilo principal | ||
+ | System.out.println(" | ||
+ | |||
+ | // Ejecutamos mientras la tarea no haya terminado | ||
+ | while (!futuro.isDone()) { | ||
+ | try { | ||
+ | System.out.println(" | ||
+ | Thread.sleep(300); | ||
+ | // Vamos sumando el tiempo de espera | ||
+ | tiempoPasado = System.currentTimeMillis() - tiempoInicio; | ||
+ | |||
+ | // 4000 hace de timeout. Pasado ese tiempo cancelamos la tarea | ||
+ | // del segundo plano | ||
+ | if (tiempoPasado > 4000) { | ||
+ | futuro.cancel(true); | ||
+ | } | ||
+ | } catch (InterruptedException ie) { | ||
+ | ie.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Llegados a este punto podemos saber si la tarea fue cancelada | ||
+ | if (futuro.isCancelled()) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | // o bien si terminó correctamente | ||
+ | else { | ||
+ | // Recogemos el resultado de la tarea Callable | ||
+ | try { | ||
+ | Boolean resultado = futuro.get(); | ||
+ | System.out.println(" | ||
+ | } catch (ExecutionException|InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Finaliza el executor. Terminará cuando todas las tareas que tiene | ||
+ | // se acaben de forma natural | ||
+ | executor.shutdown(); | ||
+ | </ | ||
+ | |||
+ | En cualquier caso, el método '' | ||
+ | |||
+ | <code java> | ||
+ | . . . | ||
+ | // Espera 10 segundos antes de lanzar la excepción TimeoutException | ||
+ | Boolean resultado = futuro.get(10, | ||
+ | . . . | ||
+ | </ | ||
+ | |||
+ | Además, es posible lanzar varias tareas al mismo tiempo con el método '' | ||
+ | |||
+ | <code java> | ||
+ | . . . | ||
+ | List< | ||
+ | List< | ||
+ | . . . | ||
+ | for (Future< | ||
+ | System.out.println(" | ||
+ | } | ||
+ | . . . | ||
+ | </ | ||
+ | |||
+ | ==== CompletableFuture ==== | ||
+ | |||
+ | Proporciona una API más completa que la que hay para la clase '' | ||
+ | |||
+ | * 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, | ||
+ | </ | ||
+ | |||
+ | * Lanzar una tarea y ejecutar algo cuando ésta termina: | ||
+ | |||
+ | <code java> | ||
+ | private String doSomethingAndReturnResult() { | ||
+ | return " | ||
+ | } | ||
+ | |||
+ | CompletableFuture.supplyAsync(() -> doSomethingAndReturnResult()) | ||
+ | .thenAccept(System.out:: | ||
+ | .whenComplete((nothing, | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== CountDownLatch ==== | ||
+ | |||
+ | Sirve como contador para bloquear una llamada hasta que éste llega a cero | ||
+ | |||
+ | Se puede utilizar para que el código del hilo principal no continúe hasta que algo se haya ejecutado por todos los hilos que son necesarios | ||
+ | |||
+ | <code java> | ||
+ | public class Tarea implements Runnable { | ||
+ | private CountDownLatch countDownLatch; | ||
+ | |||
+ | public Tarea(CountDownLatch countDownLatch) { | ||
+ | this.countDownLatch = countDownLatch; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void run() { | ||
+ | hacerAlgo(); | ||
+ | countDownLatch.countDown(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | . . . | ||
+ | . . . | ||
+ | |||
+ | CountDownLatch countDownLatch = new CountDownLatch(4); | ||
+ | for . . . | ||
+ | executorService.execute(new Tarea(countDownLatch)); | ||
+ | . . . | ||
+ | countDownLatch.await(); | ||
+ | </ | ||
+ | |||
+ | ==== CyclicBarrier ==== | ||
+ | |||
+ | Similar a '' | ||
+ | |||
+ | 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)); | ||
+ | . . . | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Colecciones sincronizadas ==== | ||
+ | |||
+ | Son lo que se conocen como colecciones // | ||
+ | |||
+ | En la clase '' | ||
+ | |||
+ | * synchronizedList() | ||
+ | * synchronizedMap() | ||
+ | * synchronizedSet() | ||
+ | |||
+ | Además, existe alguna colección ya sincronizada como '' | ||
+ | |||
+ | ==== Objetos atómicos ==== | ||
+ | |||
+ | Los objetos atómicos son estructuras Java que sirven para representar valores y realizar operaciones de forma atómica para algunos tipos de datos. | ||
+ | |||
+ | '' | ||
+ | |||
+ | Por poner algún ejemplo, '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | |||
+ | ==== 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, | ||
+ | |||
+ | < | ||
+ | {{ volatile.png }} | ||
+ | < | ||
+ | |||
+ | |||
+ | ==== 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, | ||
+ | |||
+ | 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 | ||
+ | |||
+ | < | ||
+ | {{ parallel-sequential.png }} | ||
+ | < | ||
+ | |||
+ | En los streams paralelos, se procesan los elementos de forma paralela por segmentos | ||
+ | |||
+ | <code java> | ||
+ | List< | ||
+ | // TODO Poblar la lista de ciudades | ||
+ | cities.parallelStream() | ||
+ | .forEach(System.out:: | ||
+ | </ | ||
+ | |||
+ | 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, | ||
+ | .limit(500000) | ||
+ | .parallel() | ||
+ | .filter(Main:: | ||
+ | .count(); | ||
+ | System.out.println(" | ||
+ | </ | ||
+ | |||
+ | ==== Locks ==== | ||
+ | |||
+ | Es otro mecanismo para la sincronización de hilos parecido a como hace '' | ||
+ | |||
+ | Es algo más flexible que '' | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code java> | ||
+ | Lock lock = new ReentrantLock(); | ||
+ | lock.lock(); | ||
+ | try { | ||
+ | // Aqui se accede al recurso/ | ||
+ | } finally { | ||
+ | lock.unlock(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Podemos encapsular la operación de bloqueo (lock) para evitar que se produzcan deadlocks (esperar indefinidamente un recurso) utilizando el método '' | ||
+ | |||
+ | <code java> | ||
+ | boolean isLocked = lock.tryLock(2, | ||
+ | if (isLocked) { | ||
+ | try { | ||
+ | // Aqui se accede al recurso/ | ||
+ | } finally { | ||
+ | lock.unlock(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Patrón Observer ==== | ||
+ | |||
+ | Patrón de diseño por el cual un objeto, llamada sujeto, mantiene a otros objetos, llamados observadores, | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | === 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 -= quantity; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === La clase Observer === | ||
+ | |||
+ | <code java> | ||
+ | public class Provider implements PropertyChangeListener { | ||
+ | private String name; | ||
+ | . . . | ||
+ | @Override | ||
+ | public void propertyChange(PropertyChangeEvent event) { | ||
+ | if (event.getPropertyName().equals(" | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 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); | ||
+ | } | ||
+ | </ | ||
- | \verb isAlive() | + | ---- |
+ | (c) 2021-{{date> | ||
- | ===== |
apuntes/concurrencia.1615382334.txt.gz · Last modified: 2021/03/10 13:18 by Santiago Faci