Pergunta

Eu tenho um pequeno aplicativo que tem uma rosca Render. Tudo esta discussão faz é chamar meus objetos em sua posição atual.

Eu tenho algum código como:

public void render()
{
    // ... rendering various objects

    if (mouseBall != null) mouseBall.draw()

}

Então eu também tenho alguns manipulador rato que cria e define mouseBall a uma nova bola quando o usuário clica no mouse. O usuário pode, em seguida, arraste o em torno do mouse ea bola vai seguir onde o mouse vai. Quando o usuário libera a bola Eu tenho um outro evento de mouse que conjuntos mouseBall = null.

O problema é que a minha tornar circuito está correndo rápido o suficiente para que em momentos aleatórios do condicional (mouseBall! = Null) retornará verdadeiro, mas em que fração de segundo depois desse ponto o usuário vai deixar de ir ao rato e eu vou obter uma exceção nullpointer para tentar .draw () em um objeto nulo.

O que é a solução para um problema como este?

Foi útil?

Solução

As mentiras problema no fato de que você está acessando mouseBall duas vezes, uma vez para verificar se não é null e outro para chamar uma função nele. Você pode evitar esse problema usando um temporária como este:

public void render()
{
    // ... rendering various objects
    tmpBall = mouseBall;
    if (tmpBall != null) tmpBall.draw();
}

Outras dicas

Você tem que sincronizar as declarações se e desenhar de forma que eles são garantidos para ser executado como uma seqüência atômica. Em java, isso seria feito assim:

    
public void render()
{
    // ... rendering various objects
    synchronized(this) {
        if (mouseBall != null) mouseBall .draw();
   }
}

Eu sei que você já aceitou outras respostas, mas uma terceira opção seria a utilização de classe AtomicReference do pacote java.util.concurrent.atomic. Isso proporciona a recuperação, atualização e operações comparar que atuam atomicamente sem ser necessário qualquer código de apoio. Assim, no seu exemplo:

public void render()
{
    AtomicReference<MouseBallClass> mouseBall = ...;

    // ... rendering various objects
    MouseBall tmpBall = mouseBall.get();
    if (tmpBall != null) tmpBall.draw();
}

Este é muito parecido com a solução de Greg, e conceitualmente elas são semelhantes em que, nos bastidores tanto a volatilidade do uso para garantir frescura dos valores, e tirar uma cópia temporária, a fim de aplicar uma condicional antes de usar o valor.

Por conseguinte, o exemplo exato usado aqui não é que bom para mostrar o poder dos AtomicReferences. Considere vez que o seu outro segmento irá atualizar o mouseball vairable somente se ele já foi nula - uma expressão útil para vários blocos de estilo de inicialização de código. Neste caso, ele normalmente seria imprescindível a utilização de sincronização, para assegurar que se verificou e constatou a bola foi nula, seria ainda ser nulo quando tentou defini-lo (caso contrário, você está de volta nos reinos do seu problema original). No entanto, com o AtomicReference você pode simplesmente dizer:

mouseBall.compareAndSet(null, possibleNewBall);

porque esta é uma operação atômica, por isso, se uma thread "vê" o valor como nulo que também irá configurá-lo para a referência possibleNewBall antes de quaisquer outros tópicos ter a chance de lê-lo.

Outra agradável idioma com referências atômicas é se você estiver configurando incondicionalmente alguma coisa, mas necessidade de realizar algum tipo de limpeza com o valor antigo. Caso em que você pode dizer:

   MouseBall oldBall = mouseBall.getAndSet(newMouseBall);
   // Cleanup code using oldBall

AtomicIntegers tem esses benefícios e muito mais; o método getAndIncrement () é maravilhoso para contadores globalmente partilhados, como você pode garantir cada chamada para ele retornará um valor distinto, independentemente da intercalação de threads. Segmento de segurança com um mínimo de barulho.

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