Domanda

Ho una piccola applicazione che ha un filo di rendering. Tutto questo filo non è disegnare miei oggetti alla loro posizione corrente.

Ho qualche codice come:

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

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

}

Poi ho anche un po 'di gestore del mouse che crea e imposta Mouseball a una nuova palla quando l'utente fa clic con il mouse. L'utente può quindi trascinare il mouse in giro e la palla seguirà in cui il mouse va. Quando l'utente rilascia la palla ho un altro evento del mouse che imposta Mouseball = null.

Il problema è che il mio ciclo di rendering è in esecuzione abbastanza veloce che a volte casuali condizionale (Mouseball! = Null) restituirà vero, ma in quella frazione di secondo dopo quel punto l'utente dovrà lasciare andare il mouse e io ottenere un'eccezione nullpointer per aver tentato .draw () su un oggetto nullo.

Qual è la soluzione ad un problema come questo?

È stato utile?

Soluzione

Il problema sta nel fatto che si accede mouseBall due volte, una volta per verificare se non sia null e un altro per chiamare una funzione su di esso. È possibile evitare questo problema utilizzando un temporaneo come questo:

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

Altri suggerimenti

È necessario sincronizzare il se e disegnare le dichiarazioni in modo che siano garantiti per essere eseguito come una sequenza atomica. In Java, questo potrebbe essere fatto in questo modo:

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

So che hai già accettato altre risposte, ma una terza opzione sarebbe quella di utilizzare classe AtomicReference del pacchetto di java.util.concurrent.atomic. Ciò fornisce il recupero, l'aggiornamento e confronta operazioni che agiscono atomicamente senza bisogno il codice di supporto. Quindi nel tuo esempio:

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

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

Questo sembra molto simile alla soluzione di Greg, e concettualmente sono simili in quanto dietro le quinte sia l'uso della volatilità per garantire la freschezza dei valori, e prendere una copia temporanea al fine di applicare un condizionale prima di utilizzare il valore.

Di conseguenza l'esempio esatto utilizzato qui non è buono per mostrare la potenza dei AtomicReferences. Si consideri, invece, che il vostro altro thread aggiornerà il Mouseball vairable solo se era già null - un utile linguaggio di vari blocchi di inizializzazione in stile di codice. In questo caso, sarebbe di solito è essenziale per utilizzare la sincronizzazione, per garantire che se avete controllato e trovato la palla era nulla, sarebbe ancora essere nullo quando si è tentato di impostare (altrimenti sei tornato nei regni del vostro problema originale). Tuttavia, con l'AtomicReference si può semplicemente dire:

mouseBall.compareAndSet(null, possibleNewBall);

perché questa è un'operazione atomica, quindi se un thread "vede" il valore nullo sarà anche impostare il riferimento possibleNewBall prima di qualsiasi altra thread hanno la possibilità di leggerlo.

Un altro bel linguaggio con i riferimenti atomiche è se si sta impostando incondizionatamente qualcosa, ma è necessario eseguire un qualche tipo di pulizia con il vecchio valore. In questo caso si può dire:

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

AtomicIntegers hanno questi benefici e più; il metodo getAndIncrement () è meraviglioso per contatori condivisi globalmente come si può garantire ogni chiamata verrà restituito un valore distinto, indipendentemente dal interleaving dei filetti. filo di sicurezza con un minimo sforzo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top