Tem como saber se a thread NAO conseguiu um lock?

7 respostas
kuchma

Pessoal, nao sei se isso eh avancado propriamente - talvez eu esteja com a solucao na frente do nariz e nao esteja vendo. :D

Seguinte - imagine que eu tenha um processo que pode ter apenas uma unica thread por vez rodando num determinado processo critico. Mas eu nao quero que as outras threads fiquem esperando - quero que simplesmente encerrem (se eu simplesmente sincronizasse o processo critico e ele demorasse muito, depois eu teria trocentas threads tentando pegar o lock).

Isto posto, pensei no seguinte (obvio):

- tenta pegar um lock.
- nao conseguiu, return.
- caso chegue ate aqui, rodar processo critico.

Porem como saber que a thread nao conseguiu o lock? Ela simplesmente bloqueia, certo?

Entao fiz de outra forma - controlando isso no proprio lock. Ficou meio estranho, entao queria a opiniao de voces se eh isso mesmo, se tem outra forma, se estou viajando, etc.

private static Boolean lock = new Boolean(false);
    
public void execute() {
        
    synchronized (lock) {
        if (lock.booleanValue()) {
            return; // existe outra instancia rodando no processo critico
        }
        lock = new Boolean(true);
    }

    // processo critico

    synchronized (lock) {
        lock = new Boolean(false);
    }

}

O ideal seria algo como (getLock() || return)... :D

Marcio Kuchma

7 Respostas

kuchma

Fiz uns testes bestas e aparentemente funciona.

Simulador do tal processo critico e demorado:

public class Processo {
    
    private static Boolean lock = new Boolean(false);
    
    public void initTask(String nome) {

        System.err.println("entrando - " + nome);
        
        synchronized (lock) {
            if (lock.booleanValue()) {
                System.err.println("saindo - nao conseguiu lock - " + nome);
                return;
            }
            System.err.println("conseguiu lock - " + nome);
            lock = new Boolean(true);
        }
        
        doTask();

        System.err.println("deixando lock - " + nome);
        synchronized (lock) {
            lock = new Boolean(false);
        }
            
        System.err.println("saindo - normal - " + nome);

    }
    
    private void doTask() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Apoio:

public class Runner implements Runnable {

    private String name;
    
    public Runner(String name) {
        this.name = name;
    }
    
    public void run() {
        Processo task = new Processo();
        task.initTask(name);
    }

    public static void main(String[] args) {
        Thread t = null;
        for (int i = 1; i <= 20; i++) {
            t = new Thread(new Runner(String.valueOf(i)));
            t.start();
        }
    }
    
}

A saida:

entrando - 2
conseguiu lock - 2
entrando - 3
saindo - nao conseguiu lock - 3
entrando - 4
saindo - nao conseguiu lock - 4
entrando - 5
saindo - nao conseguiu lock - 5
entrando - 6
entrando - 7
entrando - 1
entrando - 8
entrando - 9
entrando - 10
deixando lock - 2
saindo - nao conseguiu lock - 6
entrando - 11
saindo - nao conseguiu lock - 1
saindo - nao conseguiu lock - 8
saindo - nao conseguiu lock - 9
saindo - nao conseguiu lock - 10
saindo - normal - 2
conseguiu lock - 11
saindo - nao conseguiu lock - 7
entrando - 12
saindo - nao conseguiu lock - 12
entrando - 13
saindo - nao conseguiu lock - 13
deixando lock - 11
entrando - 14
conseguiu lock - 14
saindo - normal - 11
entrando - 15
saindo - nao conseguiu lock - 15
entrando - 16
saindo - nao conseguiu lock - 16
deixando lock - 14
saindo - normal - 14
entrando - 17
conseguiu lock - 17
entrando - 18
saindo - nao conseguiu lock - 18
entrando - 19
saindo - nao conseguiu lock - 19
entrando - 20
saindo - nao conseguiu lock - 20
deixando lock - 17
saindo - normal - 17

Interessante que a thread 14 pegou o lock apos a 11 deixa-lo, porem antes da 11 encerrar - isso nao eh problema, pois a 11 ja tinha saido da regiao critica.

Continuo aguardando opinioes. :D

Marcio Kuchma

louds

Não tem como fazer isso usando o monitor de cada objeto. Tua solução é usar o pacote http://gee.cs.oswego.edu/dl/ util.concurrent do Doug Lea. Ou então o equivamente java.util.concurrent do java 5.0.

Teu código passa a ficar +/- assim:

Mutex m = ...
if(m.attempt(0))
  try {
    ...
  finally { m.release(); }

Mas lembre de SEMPRE usar try/finally quando adquirir locks. Sempre, sempre, sempre. Ou então o teu código vai ter livelocks. Eu prometo que vai dar pau em produção na madrugada de sabado pra domingo se você não usar try/finally sempre.

Outra coisa, fica a dica de você verificar se não é mais negocio usar futures/executors.

kuchma

Obrigado Louds. Realmente, usando o Mutex do util.concurrent o codigo ficou mais simples (embora alguem possa argumentar que nao esta no padrao :roll: ):

ProcessoDL (versao Doug Lea do Processo):

import EDU.oswego.cs.dl.util.concurrent.Mutex;

public class ProcessoDL {
    
    private static Mutex mutex = new Mutex();
    
    public void initTask(String nome) {

        System.err.println("entrando - " + nome);
        
        try {
            if (!mutex.attempt(0)) {
                System.err.println("saindo - nao conseguiu lock - " + nome);
                return;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return;
        }

        System.err.println("conseguiu lock - " + nome);
        
        doTask();

        System.err.println("deixando lock - " + nome);
        mutex.release();
            
        System.err.println("saindo - normal - " + nome);

    }
    
    private void doTask() {
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Claro, em producao fica ainda mais simples, pois um ramo do if eh eliminado e cai fora todos esses prints. :D

Quanto aos executors, ainda nao sera dessa vez - tenho que me aprofundar nesses "concurrent idioms" para entender toda a potencia do pacote do Doug Lea (e os novos recusos do 1.5 tambem).

Marcio Kuchma

louds

kuchma, essa lib é um padrão de-facto. Sugeri ela por não saber se você tem acesso ao java 5.

Outra coisa, não esqueça do finally!!

kuchma

Duvida besta: por que tem aquela verificacao se a thread foi interrompida antes de tentar pegar o lock no Mutex do DL? Nao consegui imaginar os efeitos de deixar a thread tentar pegar o lock mesmo ela tendo sido interrompida anteriormente. Eh alguma especie de verificacao de seguranca?

Marcio Kuchma

louds

A idéia de interromper uma thread é que ela tome nota do fato o mais rápido possivel. wait() não vai lançar InterruptedException caso a thread já esteja interrompida e isso pode gerar uma demora sem limites por essa notificação.

Essa verificação existe para em um regisme de melhor esforço tentar evitar que aconteça uma pausa indesejada na thread.

Essa é uma preocupação muito importante quando se está usando o mecanismo de interrupt() para tentar minimizar o tempo de resposta, melhorar performânce e até evitar deadlocks.

kuchma

Hmmm, legal - entao isso eh relacionado com a situacao em questao, certo? (como o Mutex do DL eh generico, a preocupacao eh muito valida)

Nesse processo que estou desenvolvendo nao uso esse mecanismo de interrupcao - vou remover essa verificacao propositalmente (e entao nao precisarei cuidar da excecao). Eh que eu ja tinha lido varias coisas a respeito, mas nao tinha prestado atencao no exato conceito de “interrupcao”, do ponto de vista da classe Thread. Sao interrupcoes geradas apenas por Thread.interrupt - nada de eventos externos, preempcao, etc.

Obrigado. :smiley:

Marcio Kuchma

Criado 27 de janeiro de 2005
Ultima resposta 12 de fev. de 2005
Respostas 7
Participantes 2