Domanda

sto usando joda grazie alla sua buona reputazione per quanto riguarda Multi threading. Va grandi distanze per rendere più data threaded gestione efficiente, ad esempio rendendo Tutte Data / Ora / DateTime oggetti immutabili.

Ma qui è una situazione in cui non sono sicuro se Joda è davvero facendo la cosa giusta. Probabilmente lo fa, ma io sono molto curioso di vedere una spiegazione.

Quando un toString () di un DateTime viene chiamato Joda fa quanto segue:

/* org.joda.time.base.AbstractInstant */
public String toString() {
    return ISODateTimeFormat.dateTime().print(this);
}

Tutti i formattatori sono thread-safe (che immutabile pure), ma ciò che è circa il formattatore di fabbrica:

private static DateTimeFormatter dt;

/*  org.joda.time.format.ISODateTimeFormat */
public static DateTimeFormatter dateTime() {
    if (dt == null) {
        dt = new DateTimeFormatterBuilder()
            .append(date())
            .append(tTime())
            .toFormatter();
    }
    return dt;
}

Questo è un modello comune nelle applicazioni filettate singoli, ma è noto per essere soggetto a errori in un multithreading ambiente.

Vedo i seguenti rischi:

  • condizione Race durante il check null -> caso peggiore:. Due oggetti vengono creati

Nessun problema, in quanto questo è solo un oggetto helper (a differenza di una situazione normale pattern Singleton), uno viene salvato nel dt, l'altro è perduto e sarà garbage collection prima o poi.

    inizializzazione
  • la variabile statica potrebbe puntare a un oggetto parzialmente costruito prima della obiet è stato finito

(prima di chiamarmi pazzo, leggere una situazione simile in questo articolo di Wikipedia ).

Così come Joda garantire che nessun formattatore parzialmente creato viene pubblicato in questa variabile statica?

Grazie per le spiegazioni!

Reto

È stato utile?

Soluzione

L'hai detto, che formattatori sono di sola lettura. Se usano solo i campi finali (non ho letto una fonte formattatore) poi in 3-° edizione delle specifiche Java Language sono custoditi dalla creazione di oggetti partital da "Final campo Semantica". Non ho controllato 2-nd edition JSL e non è sicuro, se tale inizializzazione del è corretto in quella edizione.

Guardate il capitolo 17.5 e 17.5.1 in JLS. I wll costruire una "catena di eventi" per richiesto accade-prima relazione.

Prima di tutto, da qualche parte nel costruttore v'è una scrittura al campo finale nel formattatore. E 'il w scrittura. Quando costruzione viene completata, un'azione di "congelamento" ha preso plase. Chiamiamola f. Da qualche parte nel corso della fine del programma (dopo il ritorno dal costruttore, forse alcuni altri metodi e tornare da toFormatter) v'è una scrittura al campo dt. Diamo questo scrivere un nome a. Questo write (a) è dopo l'azione congelamento (f) nella "fine programma" (ordine nell'esecuzione singolo thread) e quindi f accade-prima di un (hb (f, a)) proprio per definizione JLS. Uff, inizializzazione completa ...:)

Somewtimes tardi, in un altro thread, una chiamata a dateTime (). Avviene formato. In quel momento abbiamo bisogno di due letture. Primo dei due viene letto della variabile finale nell'oggetto formattatore. Chiamiamolo r2 (per essere coerente con la JLS). Secondo dei due è una lettura del "questo" per il Formatter. Questo si verifica durante la chiamata al metodo dateTime () quando il campo dt viene letta. E chiamiamolo questa lettura r1. Che cosa abbiamo adesso? Leggi r1 visto alcuni scrittura al dt. Ritengo che tale scrittura era l'azione dal punto precedente (un solo thread ha scritto quel campo, solo per semplicità). Come r1 vedere l'una scrittura, poi c'è mc (a, r1) ( "catena Memoria" relazione, prima definizione clausola). thread corrente non ha inizializzato un formattatore, lo legge campo in azione r2 e vede un "indirizzo" di un formattatore letto al r1 azione. Così, per definizione, c'è un dereferenziazioni (r1, r2) (un'altra azione ordinare da JLS).

Abbiamo scrittura prima gelata, hb (w, f). Abbiamo congelamento prima assegnazione del dt, hb (f, a). Abbiamo una lettura da DT, mc (a, r1). E abbiamo una catena dereference tra R1 e R2, dereferenziazioni (R1, R2). Tutto questo porta ad una accade-prima relazione hb (w, r2) proprio per definizione JLS. Inoltre, per definizione, hb (d, w) dove d è una scrittura del valore predefinito per il campo finale nell'oggetto. Così, leggere r2 non può vedere scrivere w e deve vedere scrivere r2 (l'unica scrittura per il campo da un codice di programma).

Lo stesso è l'ordine per l'accesso più indiretto campo (campo finale dell'oggetto memorizzato nel campo finale, ecc ...).

Ma non è tutto! Non v'è alcun accesso a un oggetto, parzialmente costruito. Ma c'è un bug più interessante. In mancanza di qualsiasi dateTime synhronization esplicito () può restituire null. Non credo che tale comportamento può essere osservato nella pratica, ma JLS 3 ° edizione non impedisce tale comportamento. Prima lettura di campo dt nel metodo può visualizzare un valore inizializzato da un altro thread, ma secondo leggi di dt può vedere una "scrittura di valore defalut". Non accade-prima di rapporti esiste per impedirlo. Tale comportamento possibile è specifico alla versione 3-rd, seconda edizione hanno una "scrivere alla memoria principale" / "leggere dalla memoria principale" che non consente un thread per vedere un valori della variabile andando indietro nel tempo.

Altri suggerimenti

Questo è un po 'di una non-risposta, ma la spiegazione più semplice per

  

Così come Joda assicurare che non parzialmente creato formattatore viene pubblicato in questa variabile statica?

potrebbe essere solo che non garantiscono nulla, e sia gli sviluppatori non si sono resi conto che potrebbe essere un bug o ritenuto che non valeva la pena la sincronizzazione sopra.

I chiesto una domanda simile sul Joda mailing list nel 2007, anche se non ho trovato le risposte ad essere determinante e ho evitato il tempo Joda come risultato, nel bene e nel male.

La versione 3 delle garanzie linguaggio Java Spec quell'oggetto gli aggiornamenti di riferimento sono atomiche, a prescindere dal fatto che siano a 32-bit o 64-bit. Questo, combinato con gli argomenti di cui sopra, rende il codice Joda thread-safe IMO (vedi java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7)

IIRC, la versione 2 del JLS non ha incluso lo stesso chiarimento esplicito sui riferimenti agli oggetti, vale a dire solo arbitri a 32 bit sono stati garantiti atomico, quindi se si stesse utilizzando un po 'JVM 64 non c'era alcuna garanzia che avrebbe funzionato. A quel tempo stavo usando Java 1.4, che pre-datato JLS v3.

IMO il caso peggiore è non ottenere due oggetti creati, ma molti (tanti quanti sono i fili di chiamata dateTime(), per essere precisi). Poiché dateTime() non è sincronizzato e dt è né finale né volatile, una modifica del proprio valore in un thread non è garantito per essere visibile ad altri thread. Quindi, anche dopo un thread inizializzata dt, qualsiasi numero di altri thread può ancora vedere il riferimento nullo, creare così felicemente nuovi oggetti.

Oltre a questo, come spiegato da altri, un oggetto parzialmente creato può non ottenere pubblicato da dateTime(). Né un parzialmente modificato (= penzoloni) di riferimento, poiché gli aggiornamenti del valore di riferimento sono garantiti per essere atomica.

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