Java 1,4 sincronização: só permitem um exemplo de método de funcionamento (não bloqueio)?

StackOverflow https://stackoverflow.com/questions/152138

Pergunta

Eu tenho uma classe propondo traduções utilitários. As traduções si deve ser recarregado a cada 30 minutos. Eu uso o apoio da Primavera temporizador para isso. Basicamente, meus classe se parece com:

public interface Translator {
    public void loadTranslations();
    public String getTranslation(String key);
}

loadTranslations () pode ser bastante longo para correr, por isso, enquanto ele está executando as traduções antigas ainda estão disponíveis. Isto é feito através do carregamento das traduções em um mapa local e apenas mudando a referência quando todas as traduções são carregados.

O meu problema é:. Como posso ter certeza de que quando um fio já está a carregar traduções, é um segundo também tenta correr, ele detecta isso e retorna imediatamente, sem iniciar uma segunda atualização

Um método sincronizado somente a fila das cargas ... Eu ainda estou em Java 1.4, de modo nenhum java.util.concurrent.

Obrigado por sua ajuda!

Foi útil?

Solução

Use algum tipo de mecanismo de bloqueio para executar apenas a tarefa se não estiver já em andamento. Adquirir o sinal de bloqueio tem de ser um processo de um só passo. Veja:

/**
 * @author McDowell
 */
public abstract class NonconcurrentTask implements Runnable {

    private boolean token = true;

    private synchronized boolean acquire() {
        boolean ret = token;
        token = false;
        return ret;
    }

    private synchronized void release() {
        token = true;
    }

    public final void run() {
        if (acquire()) {
            try {
                doTask();
            } finally {
                release();
            }
        }
    }

    protected abstract void doTask();

}

Test código que irá lançar uma exceção se a tarefa é executada simultaneamente:

public class Test {

    public static void main(String[] args) {
        final NonconcurrentTask shared = new NonconcurrentTask() {
            private boolean working = false;

            protected void doTask() {
                System.out.println("Working: "
                        + Thread.currentThread().getName());
                if (working) {
                    throw new IllegalStateException();
                }
                working = true;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (!working) {
                    throw new IllegalStateException();
                }
                working = false;
            }
        };

        Runnable taskWrapper = new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    shared.run();
                }
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(taskWrapper).start();
        }
    }

}

Outras dicas

Eu sou de um fundo .net (nenhuma experiência java em tudo), mas você pode tentar uma bandeira estático simples de algum tipo que verifica no início do método se a sua execução alrady. Então tudo que você precisa fazer é certificar-se de qualquer leitura / escrita de que a bandeira é sincronizado. Então, no início verificar a bandeira, se não for definido, configurá-lo, se estiver definido, o retorno. Se não é definido, execute o resto do método, e após a sua completa, unset-lo. Apenas certifique-se de colocar o código em um try / finally ea bandeira iunsetting no finalmente por isso sempre fica desactivado em caso de erro. Muito simplificado, mas pode ser tudo que você precisa.

Edit: Isso realmente provavelmente funciona melhor do que a sincronização do método. Porque você realmente precisa de uma nova tradução imediatamente , após o que antes dela termina? E você não pode querer prender um fio por muito tempo se tem que esperar um pouco.

Mantenha uma alça sobre o fio de carga para ver se ele está funcionando?

Ou você não pode simplesmente usar uma bandeira sincronizado para indicar se uma carga está em andamento?

Esta é realmente idêntico ao código que é necessário para gerenciar a construção de um Singleton quando feito da maneira clássica (surpresa!):

if (instance == null) {
  synchronized {
    if (instance == null) {
       instance = new SomeClass();
    }
  }
}

O teste interior é idêntico ao ensaio de exterior. O teste externo é para que nós não rotineiramente entrar em um bloco sincronizado, o teste interno é para confirmar que a situação não mudou desde a última vez que fez o teste (o fio poderia ter sido preterido antes de entrar sincronizada).

No seu caso:

if (translationsNeedLoading()) {
  synchronized {
    if (translationsNeedLoading()) {
       loadTranslations();
    }
  }
}

UPDATE: Esta maneira de construir um singleton não irá funcionar de forma confiável sob seu JDK1.4. Para explicação ver aqui . No entanto, eu acho que você é que você vai ficar bem neste cenário.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top