Sincronização de Threads

Introdução

O exemplo Produtor/Consumidor

public class Producer extends Thread {
    private CubbyHole cubbyhole;
    private int number;

    public Producer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(number, i);
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
}
public class Consumer extends Thread {
    private CubbyHole cubbyhole;
    private int number;

    public Consumer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    public void run() {
        int value = 0;
        for (int i = 0; i < 10; i++) {
            value = cubbyhole.get(number);
        }
    }
}

threads-consumer1.gif (2668 bytes)

threads-consumer2.gif (2738 bytes)

A primeira sincronização: travando um objeto (locking)

public class CubbyHole {
    private int contents;
    private boolean available = false;

    public synchronized int get(int who) {
        ...
    }

    public synchronized void put(int who, int value) {
        ...
    }
}
   // ...
   synchronized {
       // qualquer conjunto de statements
   }
   // ...
   synchronized(obj) {
       // qualquer conjunto de statements
   }

Readquirindo um lock

public class Reentrant {
    public synchronized void a() {
        b();
        System.out.println("here I am, in a()");
    }
    public synchronized void b() {
        System.out.println("here I am, in b()");
    }
}
here I am, in b()
here I am, in a()

A segunda sincronização: usando os métodos notifyAll e wait

public synchronized int get() {    //won't work!
    if (available == true) {
        available = false;
        return contents;
    }
}
public synchronized void put(int value) {    //won't work!
    if (available == false) {
        available = true;
        contents = value;
    }
}
public synchronized int get() {
    while (available == false) {
        try {
            //wait for Producer to put value
            wait();
        } catch (InterruptedException e) { }
    }
    available = false;
    //notify Producer that value has been retrieved
    notifyAll();
    return contents;
}

public synchronized void put(int value) {
    while (available == true) {
        try {
            //wait for Consumer to get value
            wait();
        } catch (InterruptedException e) { }
    }
    contents = value;
    available = true;
    //notify Consumer that value has been set
    notifyAll();
}

Executando o exemplo Produtor/Consumidor

public class ProducerConsumerTest {
    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();
        Producer p1 = new Producer(c, 1);
        Consumer c1 = new Consumer(c, 1);

        p1.start();
        c1.start();
    }
}
Producer #1 put: 0
Consumer #1 got: 0
Producer #1 put: 1
Consumer #1 got: 1
Producer #1 put: 2
Consumer #1 got: 2
Producer #1 put: 3
Consumer #1 got: 3
Producer #1 put: 4
Consumer #1 got: 4
Producer #1 put: 5
Consumer #1 got: 5
Producer #1 put: 6
Consumer #1 got: 6
Producer #1 put: 7
Consumer #1 got: 7
Producer #1 put: 8
Consumer #1 got: 8
Producer #1 put: 9
Consumer #1 got: 9

Locks explícitos e variáveis condicionais

Métodos Condition.await
Método Descrição
await Espera uma condição ocorrer
awaitUninterruptibly Espera uma condição ocorrer. Não pode ser interrompido.
awaitNanos(long timeout) Espera uma condição ocorrer. Espera no máximo timeout nanossegundos
await(long timeout, TimeUnit unit) Espera uma condição ocorrer. Espera no máximo timeout TimeUnit
await(Date timeout) Espera uma condição ocorrer. Espera no máximo até a data especificada
import java.util.concurrent.locks.*;
public class CubbyHole2 {
    private int contents;
    private boolean available = false;
    private Lock aLock = new ReentrantLock();
    private Condition condVar = aLock.newCondition();

    public int get(int who) {
        aLock.lock();
        try {
            while (available == false) {
                try {
                    condVar.await();
                } catch (InterruptedException e) { }
            }
            available = false;
            System.out.println("Consumer " + who + " got: " +
                                contents);
            condVar.signalAll();
        } finally {
            aLock.unlock();
            return contents;
        }
    }

    public void put(int who, int value) {
                    aLock.lock();
    try {
        while (available == true) {
            try {
                condVar.await();
            } catch (InterruptedException e) { }
        }
        contents = value;
        available = true;
        System.out.println("Producer " + who + " put: " +
                            contents);
        condVar.signalAll();
        } finally {
            aLock.unlock();
        }
    }
}

Estruturas de dados sincronizadas

import java.util.concurrent.*;
public class Producer3 extends Thread {
    private BlockingQueue cubbyhole;
    private int number;
                          
    public Producer3(BlockingQueue c, int num) {
        cubbyhole = c;
        number = num;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                cubbyhole.put(i);
                System.out.println("Producer #" + number + 
                                   " put: " + i);
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
}
import java.util.concurrent.*;
public class ProducerConsumerTest3 {
    public static void main(String[] args) {

        BlockingQueue c = new ArrayBlockingQueue(1);
        Producer3 p1 = new Producer3(c, 1);
        Consumer3 c1 = new Consumer3(c, 1);

        p1.start();
        c1.start();
    }
}

Sincronizando coleções

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

Starvation (morte por inanição) e deadlock

programa