Se uma thread 1 encontra em sua execução um bloco de código synchronized cujo lock já foi obtido por outra thread 2, existe como a thread 1 pular a execução do código, ou seja, fazer a thread 1 desistir de ficar na fila para obter o lock?
Em outras palavras, existe como fazer um bloco de código ser executado apenas pela primeira Thread que obter o lock?
Eu preciso fazer uma barbearia. Os barbeiros são threads que percorrem a barbearia em busca de clientes esperando atendimento. Só que 2 barbeiros não podem verificar se um mesmo cliente está sendo atendido e barbeá-lo, então eu fiz esse trecho synchonized, só que assim um barbeiro fica esperando o outro terminar para verificar que o cliente em questão foi barbeado, quando deveria partir pra outro.
Tá aqui o código, a parte sincronizada tá na linha 76:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Barbearia {
static int DELAY = 5000;
private int quantosBarbeiros;
private Collection<Barbeiro> barbeiros;
private Collection<Cliente> clientes;
public Barbearia(int quantosBarbeiros) {
this.clientes = new ArrayList<Cliente>();
this.quantosBarbeiros = quantosBarbeiros;
}
public void entrar(Cliente cliente) {
clientes.add(cliente);
System.out.printf("%s chegou à barbearia\n", cliente);
if (barbeiros == null) {
barbeiros = new ArrayList<Barbeiro>();
for (int i = 0; i < quantosBarbeiros; i++)
barbeiros.add(new Barbeiro(this));
}
}
public Collection<Cliente> getClientes() {
return clientes;
}
/* public Collection<Cliente> fila() {
Collection<Cliente> fila = new ArrayList<Cliente>();
for (Iterator<Cliente> iterator = clientes.iterator(); iterator.hasNext();) {
Cliente cliente = iterator.next();
if (cliente.getBarbeiro() == null)
fila.add(cliente);
iterator = clientes.iterator();
}
return fila;
} */
public static void main(String[] args) {
Barbearia barbearia = new Barbearia(4);
for (int i = 0; i < 4; i++)
new Cliente(barbearia);
}
}
class Barbeiro extends Thread {
private int id;
private Barbearia barbearia;
private static int contador = 0;
public Barbeiro(Barbearia barbearia) {
id = ++contador;
this.barbearia = barbearia;
start();
}
@Override
public void run() {
Collection<Cliente> clientes = barbearia.getClientes();
while (clientes.size() > 0) {
for (Iterator<Cliente> iterator = clientes.iterator(); iterator.hasNext();) {
Cliente cliente = iterator.next();
verificarCliente(cliente);
iterator = clientes.iterator();
}
}
}
private void verificarCliente(Cliente cliente){
synchronized (cliente) {
if (cliente.getBarbeiro() == null)
barbear(cliente);
}
}
private void barbear(Cliente cliente) {
cliente.setBarbeiro(this);
try {
System.out.printf("%s vai barbear cliente %s\n", this, cliente);
Thread.sleep((int) (Barbearia.DELAY));
barbearia.getClientes().remove(cliente);
System.out.printf("%s terminou de barbear cliente %s\n", this, cliente);
} catch (InterruptedException e) {
System.out.printf("ERRO: %s não pôde barbear o cliente %s\n", this, cliente);
}
// Setar como null significa que o cliente entrou na fila de novo :)
//cliente.setBarbeiro(null);
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + id;
}
}
class Cliente extends Thread {
private int id;
private Barbearia barbearia;
private Barbeiro barbeiro;
private static int contador = 0;
public Cliente(Barbearia barbearia) {
id = ++contador;
this.barbearia = barbearia;
start();
}
@Override
public void run() {
try {
System.out.printf("%s está indo à barbearia\n", this);
Thread.sleep((int) (Math.random() * Barbearia.DELAY));
barbearia.entrar(this);
} catch (InterruptedException e) {
System.out.println("ERRO: %s não pôde ir à barbearia.");
}
}
public Barbearia getBarbearia() {
return barbearia;
}
public void setBarbearia(Barbearia barbearia) {
this.barbearia = barbearia;
}
public Barbeiro getBarbeiro() {
return barbeiro;
}
public void setBarbeiro(Barbeiro barbeiro) {
this.barbeiro = barbeiro;
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + id;
}
}