La sincronització és un concepte clau en la programació multithreading que permet controlar l'accés concurrent a recursos compartits. Sense una sincronització adequada, els fils poden interferir entre ells, causant errors com condicions de carrera, inconsistències de dades i altres problemes difícils de diagnosticar.

Conceptes Clau

  1. Condicions de Carrera: Situació en què dos o més fils accedeixen a dades compartides i intenten canviar-les simultàniament.
  2. Bloqueig (Lock): Mecanisme que permet a un fil obtenir l'accés exclusiu a un recurs compartit.
  3. Monitor: Estructura que permet la sincronització de fils mitjançant l'ús de bloquejos i condicions.

Paraula Clau synchronized

En Java, la paraula clau synchronized s'utilitza per controlar l'accés a blocs de codi o mètodes que han de ser executats per un sol fil a la vegada.

Sincronització de Mètodes

Quan un mètode està marcat com synchronized, el fil que l'executa obté el bloqueig del monitor de l'objecte abans d'executar el mètode. Altres fils han d'esperar fins que el bloqueig sigui alliberat.

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Sincronització de Blocs de Codi

És possible sincronitzar només una part d'un mètode utilitzant blocs de codi sincronitzats. Això permet una major flexibilitat i pot millorar el rendiment.

public class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        synchronized (this) {
            return count;
        }
    }
}

Sincronització Estàtica

Els mètodes estàtics també poden ser sincronitzats. En aquest cas, el bloqueig es fa sobre la classe en lloc de l'objecte.

public class StaticCounter {
    private static int count = 0;

    public static synchronized void increment() {
        count++;
    }

    public static synchronized int getCount() {
        return count;
    }
}

Exemples Pràctics

Exemple 1: Condició de Carrera

Sense sincronització, els fils poden interferir entre ells, causant resultats inesperats.

public class RaceConditionExample {
    private int count = 0;

    public void increment() {
        count++;
    }

    public static void main(String[] args) {
        RaceConditionExample example = new RaceConditionExample();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.count);
    }
}

Exemple 2: Solució amb Sincronització

Afegint sincronització, podem evitar la condició de carrera.

public class SynchronizedExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.count);
    }
}

Exercicis Pràctics

Exercici 1: Sincronització de Mètodes

Crea una classe BankAccount amb els següents mètodes sincronitzats:

  • deposit(int amount): Afegeix una quantitat al saldo.
  • withdraw(int amount): Resta una quantitat del saldo si hi ha prou fons.
  • getBalance(): Retorna el saldo actual.

Solució

public class BankAccount {
    private int balance = 0;

    public synchronized void deposit(int amount) {
        balance += amount;
    }

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }

    public synchronized int getBalance() {
        return balance;
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount();

        Runnable depositTask = () -> {
            for (int i = 0; i < 1000; i++) {
                account.deposit(1);
            }
        };

        Runnable withdrawTask = () -> {
            for (int i = 0; i < 1000; i++) {
                account.withdraw(1);
            }
        };

        Thread thread1 = new Thread(depositTask);
        Thread thread2 = new Thread(withdrawTask);

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final balance: " + account.getBalance());
    }
}

Resum

En aquesta secció, hem après sobre la importància de la sincronització en la programació multithreading per evitar condicions de carrera i altres problemes de concurrència. Hem vist com utilitzar la paraula clau synchronized per sincronitzar mètodes i blocs de codi, així com exemples pràctics per il·lustrar aquests conceptes. La sincronització és essencial per garantir que els recursos compartits siguin accessibles de manera segura per múltiples fils.

Curs de Programació en Java

Mòdul 1: Introducció a Java

Mòdul 2: Flux de Control

Mòdul 3: Programació Orientada a Objectes

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

Mòdul 8: Multithreading i Concurrència

Mòdul 9: Xarxes

Mòdul 10: Temes Avançats

Mòdul 11: Frameworks i Llibreries de Java

Mòdul 12: Construcció d'Aplicacions del Món Real

© Copyright 2024. Tots els drets reservats