viernes, 19 de octubre de 2012

Ejemplo exclusión mutua (synchronized)

En este ejemplo vamos a tener 3 clases: una clase Main, una clase CuentaBanco y una clase VerificarCuenta (Hilo).

Clase Main:

public class Main {
    public static void main(String[] args) {
       
        VerificarCuenta vc = new VerificarCuenta();
       
        Thread Luis = new Thread(vc, "Luis");
        Thread Manuel = new Thread(vc, "Manuel");
       
        Luis.start();
        Manuel.start();
    }
}


Clase CuentaBanco:

public class CuentaBanco {
    private int balance = 50;
   
    public CuentaBanco(){       
    }

    public int getBalance(){
        return balance;
    }

    public void retiroBancario(int retiro){
        balance = balance - retiro;
    }

}

Clase VerificarCuenta (Hilo):

public class VerificarCuenta implements Runnable{

private  CuentaBanco cb = new CuentaBanco();
   
    private void HacerRetiro(int cantidad) throws InterruptedException{       
           
        if(cb.getBalance() >= cantidad){
            System.out.println(Thread.currentThread().getName() + " está realizando un retiro de: " + cantidad + ".");

            Thread.sleep(1000);
            cb.retiroBancario(cantidad);

            System.out.println(Thread.currentThread().getName() + ": Retiro realizado.");
            System.out.println(Thread.currentThread().getName() + ": Los fondos son de: " + cb.getBalance());

        }else{           
            System.out.println("No hay suficiente dinero en la cuenta para realizar el retiro Sr." + Thread.currentThread().getName());
            System.out.println("Su saldo actual es de "+cb.getBalance());
            Thread.sleep(1000);
        }       
      
    }
   
    @Override
    public void run() {
        for (int i = 0; i <= 3; i++) {
            try {
                this.HacerRetiro(10);
                if(cb.getBalance() < 0){
                    System.out.println("La cuenta está sobregirada.");
                }
            } catch (InterruptedException ex) {
                Logger.getLogger(VerificarCuenta.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
   
}

Si realizamos lo anterior, nos aparece un resultado parecido a lo siguiente:

Luis está realizando un retiro de: 10.
Manuel está realizando un retiro de: 10.
Luis: Retiro realizado.
Luis: Los fondos son de: 40
Luis está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 30
Manuel está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 20
Manuel está realizando un retiro de: 10.
Luis: Retiro realizado.
Luis: Los fondos son de: 10
Luis está realizando un retiro de: 10.
Luis: Retiro realizado.
Luis: Los fondos son de: 0
No hay suficiente dinero en la cuenta para realizar el retiro Sr.Luis
Su saldo actual es de 0
Manuel: Retiro realizado.
Manuel: Los fondos son de: -10
La cuenta está sobregirada.
No hay suficiente dinero en la cuenta para realizar el retiro Sr.Manuel
Su saldo actual es de -10
La cuenta está sobregirada.
La cuenta está sobregirada.

Desastroso no?, mientras Luis estaba chequeando el estado de cuenta y vio que era posible el retiro, Manuel estaba retirando y viceversa, finalmente, Manuel verificó que había 10 pesos en el saldo y decidió retirarlos, pero oh sorpresa! Luis los acababa de retirar, sin embargo el retiro de Manuel también se completó dejando la cuenta sobregirada.

¿Qué hacemos para proteger los datos? Dos cosas:

- Marcar las variables como privadas.
- Sincronizar el código que modifica las variables.

Para marcar las variables como privadas utilizamos los identificadores de control de acceso, en este caso la palabra private. Para sincronizar el código utilizamos la palabra synchronized. Dicho con código...

private synchronized void HacerRetiro(int cantidad) throws InterruptedException{
       
           
        if(cb.getBalance() >= cantidad){
            System.out.println(Thread.currentThread().getName() + " está realizando un retiro de: " + cantidad + ".");
            Thread.sleep(1000);
            cb.retiroBancario(cantidad);
            System.out.println(Thread.currentThread().getName() + ": Retiro realizado.");
            System.out.println(Thread.currentThread().getName() + ": Los fondos son de: " + cb.getBalance());
        }else{           
            System.out.println("No hay suficiente dinero en la cuenta para realizar el retiro Sr." + Thread.currentThread().getName());
            System.out.println("Su saldo actual es de "+cb.getBalance());
            Thread.sleep(1000);
        }       
      
    }

Retomemos lo anterior. El método que realiza las operaciones con las variables es hacerRetiro(), por lo tanto, es el método que necesitamos que sea privado y sincronizado. Todo lo demás permanece igual. Si lo ejecutamos, obtenemos algo parecido a lo siguiente:

Luis está realizando un retiro de: 10.
Luis: Retiro realizado.
Luis: Los fondos son de: 40
Manuel está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 30
Manuel está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 20
Manuel está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 10
Manuel está realizando un retiro de: 10.
Manuel: Retiro realizado.
Manuel: Los fondos son de: 0
No hay suficiente dinero en la cuenta para realizar el retiro Sr.Luis
Su saldo actual es de 0
No hay suficiente dinero en la cuenta para realizar el retiro Sr.Luis
Su saldo actual es de 0
No hay suficiente dinero en la cuenta para realizar el retiro Sr.Luis
Su saldo actual es de 0

6 comentarios:

  1. Gracias por este ejemplo tan sencillo, había visto otro pero era demasiado complicado y no entendia bien la funcion "synchronized", sigan asi.

    ResponderEliminar
  2. Muy buen ejemplo, el profesor lo esta explicando con un ejempl similar, lo importante es sincronizar las retiradas para que sean secuenciales

    ResponderEliminar
  3. Gracias por el ejemplo, me ayudo a comprender mejor la palabra reservada Syncronized, una curiosidad mi ejecución se comporto diferente, sera por mi maquina, es una intel core5, 8GB de ram, eso afecto el comportamiento?

    Luis esta realizando un retiro de 10.
    Luis :Retiro realizado.!!
    Luis :Los fondos son de: 40
    Luis esta realizando un retiro de 10.
    Luis :Retiro realizado.!!
    Luis :Los fondos son de: 30
    Luis esta realizando un retiro de 10.
    Luis :Retiro realizado.!!
    Luis :Los fondos son de: 20
    Luis esta realizando un retiro de 10.
    Luis :Retiro realizado.!!
    Luis :Los fondos son de: 10
    Manuel esta realizando un retiro de 10.
    Manuel :Retiro realizado.!!
    Manuel :Los fondos son de: 0
    No hay suficiente dinero en la cuenta para realizar retiro Sr. Manuel
    Su saldo actual es de 0
    No hay suficiente dinero en la cuenta para realizar retiro Sr. Manuel
    Su saldo actual es de 0
    No hay suficiente dinero en la cuenta para realizar retiro Sr. Manuel
    Su saldo actual es de 0

    ResponderEliminar