Devaneio thredáticos

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;
	}
		
}

Apenas lendo o problema, pensei na seguinte forma: cria uma váriavel estática booleana que verifica se ja tem um barbeiro atndendo o cliente. Dessa forma o barbeiro só chama o método se o cliente estiver livre. E quando ele começa a atender o cliente ele muda o valor do boolean.

[]'s

Lê de novo.

Renato, o que você está procurando é uma work queue. É uma maneira bem simples de montar um pool de threads para, por exemplo, servir requisições HTTP.

Cara, li de novo e li tudo agora!
O que te falei funciona, e a sua solução também! O problema é que você está colocando a operação de barbear dentro da verificação. Muda seu código para a seguinte forma e testa:

public void run() {   
        Collection<Cliente> clientes = barbearia.getClientes();   
        while (clientes.size() > 0) {   
            for (Iterator<Cliente> iterator = clientes.iterator(); iterator.hasNext();) {   
                Cliente cliente = iterator.next();   
                if (verificarCliente(cliente)) {
                    barbear(cliente);
                }
                iterator = clientes.iterator();   
            }         
        }                 
    }   
       
    private synchronized void verificarCliente(Cliente cliente){   
            if (cliente.getBarbeiro() == null) {
                cliente.setBarbeiro(this);
                return true;
            }
            return false;   
              
    }   
} 

Ve se funciona. A worker thread sugerida acima também funciona.

Recomeçei tudo do zero sem threads e agora acho q resolveu…

Um lance novo agora é que tem uma hora que eu tenho que chamar run() direto porque senão a thread vai startar recursivamente :shock:

Opa, tem truta aí…posta o código!Será que não há uma thread “roubando” o lock antes da hora?

É tipo assim: a primeira vez que o barbeiro atende, tem que ser um start(), pois quando ele termina, ele mesmo vai puxando os outros da fila, ao contrário do primeiro cliente que é “empurrado” pra ele. O start é para que não fique apenas um barbeiro trabalhando e os outros parados. Só que nos próximos clientes eu tenho que chamar o método de barbear de novo para outro cliente, e como a thread já está rodando, eu chamo run() diretamente:

[code]import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Barbearia {

static final int DELAY = 1;
private int barbeados = 0;
private Collection<Barbeiro> barbeiros;
private Fila<Cliente> fila;

public Barbearia(int quantosBarbeiros) {
	barbeiros = new ArrayList<Barbeiro>();		
	for (int i = 0; i < quantosBarbeiros; i++) 
		barbeiros.add(new Barbeiro(this));
	fila = new Fila<Cliente>();
}
	
public void entrar(Cliente cliente) {
	System.out.printf("%s chegou à barbearia\n", cliente);
	Cliente.notificarEntrada(cliente);
	Barbeiro barbeiro = providenciarAtendimento();
	if (barbeiro != null)
		barbeiro.barbear(cliente);
	else
		fila.adicionar(cliente);
}

private Barbeiro providenciarAtendimento() {
	for (Iterator<Barbeiro> iterador = barbeiros.iterator(); iterador.hasNext();) {
		Barbeiro barbeiro = iterador.next();
			if (barbeiro.livre())
				return barbeiro;					
	}
	return null;
}

public void notificarDisponibilidade(Barbeiro barbeiro) {
	if (barbeiro == null)
		return;
	barbeados++;
	if (fila.existe())
		barbeiro.barbear(fila.proximo());
}

private void avisarQuandoAcabarExpediente() {
	while (Thread.activeCount() != 1); 
	System.out.printf("########## %d clientes foram barbeados\n", barbeados);
}

public static void main(String[] args) {		
	final int QUANTOS_CLIENTES = 40;
	final int QUANTOS_BARBEIROS = 4;
	
	Barbearia barbearia = new Barbearia(QUANTOS_BARBEIROS);
	Cliente.iniciarClientes(barbearia, QUANTOS_CLIENTES);
	barbearia.avisarQuandoAcabarExpediente();
}

}

class Barbeiro extends Thread {

private static int contador = 0;
private int id;
private int clientesAtendidos;
private Cliente cliente;
private Barbearia barbearia;

public Barbeiro(Barbearia barbearia) {
	id = ++contador;
	this.barbearia = barbearia;
	clientesAtendidos = 0;
}

public void run() {
	if (cliente == null) {
		System.out.printf("ERRO: %s não pode barbear o vácuo\n", this);
		return;
	}
	
	System.out.printf("%s vai barbear cliente %s\n", this, cliente);
	try {			
		Thread.sleep(Barbearia.DELAY);
	} catch (InterruptedException e) {}
	System.out.printf("%s terminou de barbear cliente %s\n", this, cliente);
	
	clientesAtendidos++;
	this.cliente = null;
	barbearia.notificarDisponibilidade(this);
}

public synchronized boolean barbear(Cliente cliente) {
	boolean meAloquei = alocarPara(cliente);
	if (meAloquei) {
		if (nenhumClienteAtendido())
			start();
		else
			run();
	}
	return meAloquei;
}

private boolean nenhumClienteAtendido() {
	return clientesAtendidos < 1;
}
	
private boolean alocarPara(Cliente cliente) {
	if (this.cliente == null) {
		this.cliente = cliente;
		return true;
	}
	return false;
}

public boolean livre() {
	return cliente == null;
}
		
@Override
public String toString() {
	return getClass().getSimpleName() + " " + id;
}

}

class Cliente extends Thread {

private static int contador = 0;
private static int quantosChegaram = 0;
private Barbearia barbearia;
private int id;	

private Cliente(Barbearia barbearia) {
	id = ++contador;
	this.barbearia = barbearia;
	start();
}

public void run() {
	System.out.printf("%s está indo à barbearia\n", this);
	try {			
		Thread.sleep((int) (Math.random() * Barbearia.DELAY));
	} catch (InterruptedException e) {}
	barbearia.entrar(this);
}

public static void notificarEntrada(Cliente cliente) {
	quantosChegaram++;
}

public static void iniciarClientes(Barbearia barbearia, int quantos) {
	for (int i = 0; i < quantos; i++)  
		new Cliente(barbearia);
	avisarQuandoChegarem(quantos);
}

public static void avisarQuandoChegarem(int quantos) {
	while (Cliente.quantosChegaram < quantos);
	System.out.printf("########## %d clientes chegaram à barbearia\n", quantos);				
}

@Override
public String toString() {
	return getClass().getSimpleName() + " " + id;
}

}

class Fila {
private List elementos;
public Fila() { elementos = new LinkedList(); }
public boolean adicionar(T obj) { return elementos.add(obj); }
public T proximo() { return elementos.remove(0); }
public int tamanho() { return elementos.size(); }
public boolean existe() { return tamanho() > 0; }
}
[/code]

this.up(ironlynx)