En aquest tema, explorarem les utilitats de concurrència proporcionades per la biblioteca java.util.concurrent de Java. Aquestes utilitats faciliten la gestió de fils i la sincronització en aplicacions multithreading, millorant la seguretat i l'eficiència del codi.
Continguts
- Introducció a
java.util.concurrent - Executors
- Col·leccions Concurrent
- Locks
- Barriers i Latches
- Exemples Pràctics
- Exercicis
- Introducció a
java.util.concurrent
java.util.concurrentLa biblioteca java.util.concurrent proporciona una sèrie de classes i interfícies per gestionar la concurrència de manera més eficient i segura. Algunes de les funcionalitats clau inclouen:
- Executors: Per gestionar grups de fils.
- Col·leccions Concurrent: Per a col·leccions segures en entorns multithreading.
- Locks: Per a mecanismes de sincronització més flexibles que els blocs sincronitzats.
- Barriers i Latches: Per a la coordinació de fils.
- Executors
Els executors són una manera d'abstraure la creació i gestió de fils. La interfície ExecutorService és la més utilitzada.
Exemple de codi:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(new Task(i));
}
executor.shutdown();
}
}
class Task implements Runnable {
private int taskId;
public Task(int id) {
this.taskId = id;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is running.");
}
}Explicació:
- ExecutorService: Crea un grup de fils amb una mida fixa.
- submit: Envia tasques al grup de fils per a la seva execució.
- shutdown: Tanca l'executor després de completar totes les tasques.
- Col·leccions Concurrent
Les col·leccions concurrent proporcionen versions segures de col·leccions comunes com ara List, Map, Queue, etc.
Exemple de codi:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("One", 1);
map.put("Two", 2);
System.out.println(map.get("One"));
}
}Explicació:
- ConcurrentHashMap: Una implementació segura de
HashMapper a entorns multithreading.
- Locks
Els locks proporcionen un control més flexible sobre la sincronització que els blocs sincronitzats.
Exemple de codi:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final Lock lock = new ReentrantLock();
public void performTask() {
lock.lock();
try {
// Codi crític
System.out.println("Task is being performed.");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
LockExample example = new LockExample();
example.performTask();
}
}Explicació:
- ReentrantLock: Un tipus de lock que permet a un fil adquirir el lock diverses vegades.
- lock: Adquireix el lock.
- unlock: Allibera el lock.
- Barriers i Latches
Les barriers i latches són mecanismes per coordinar la finalització de múltiples fils.
Exemple de codi amb CountDownLatch:
import java.util.concurrent.CountDownLatch;
public class LatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(new Worker(latch)).start();
}
latch.await();
System.out.println("All tasks are completed.");
}
}
class Worker implements Runnable {
private final CountDownLatch latch;
public Worker(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
System.out.println("Task is running.");
latch.countDown();
}
}Explicació:
- CountDownLatch: Permet que un o més fils esperin fins que un conjunt d'operacions en altres fils es completin.
- countDown: Decrementa el comptador del latch.
- await: Espera fins que el comptador arribi a zero.
- Exemples Pràctics
ExecutorService amb Callable:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> future = executor.submit(new Task());
try {
Integer result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() {
return 123;
}
}Explicació:
- Callable: Similar a
Runnable, però pot retornar un resultat i llençar excepcions. - Future: Representa el resultat d'una operació asincrònica.
- Exercicis
Exercici 1: Utilitzar ExecutorService
Crea un programa que utilitzi ExecutorService per executar 10 tasques en paral·lel. Cada tasca ha de imprimir el seu identificador i dormir durant 1 segon.
Solució:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExercise {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int id = i;
executor.submit(() -> {
System.out.println("Task " + id + " is running.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}Exercici 2: Utilitzar CountDownLatch
Crea un programa que utilitzi CountDownLatch per esperar que 5 fils completin la seva tasca abans de continuar.
Solució:
import java.util.concurrent.CountDownLatch;
public class LatchExercise {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println("Task is running.");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All tasks are completed.");
}
}Conclusió
En aquest tema, hem explorat les utilitats de concurrència proporcionades per la biblioteca java.util.concurrent. Hem après a utilitzar ExecutorService per gestionar fils, col·leccions concurrent per a la seguretat en entorns multithreading, locks per a la sincronització flexible, i mecanismes de coordinació com CountDownLatch. Aquests conceptes són fonamentals per escriure aplicacions multithreading eficients i segures en Java.
Curs de Programació en Java
Mòdul 1: Introducció a Java
- Introducció a Java
- Configuració de l'Entorn de Desenvolupament
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Operadors
Mòdul 2: Flux de Control
Mòdul 3: Programació Orientada a Objectes
- Introducció a la POO
- Classes i Objectes
- Mètodes
- Constructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
Mòdul 4: Programació Orientada a Objectes Avançada
Mòdul 5: Estructures de Dades i Col·leccions
Mòdul 6: Gestió d'Excepcions
Mòdul 7: Entrada/Sortida de Fitxers
- Lectura de Fitxers
- Escriptura de Fitxers
- Fluxos de Fitxers
- BufferedReader i BufferedWriter
- Serialització
Mòdul 8: Multithreading i Concurrència
- Introducció al Multithreading
- Creació de Fils
- Cicle de Vida dels Fils
- Sincronització
- Utilitats de Concurrència
Mòdul 9: Xarxes
- Introducció a les Xarxes
- Sockets
- ServerSocket
- DatagramSocket i DatagramPacket
- URL i HttpURLConnection
