2016-03-15 14 views
5
public void tSafe(List<Foo> list, Properties status) { 
    if(list == null) return; 
    String key = "COUNT"; 
    AtomicInteger a = new AtomicInteger(Integer.valueOf(status.getProperty(key,"0"))); 
    list.parallelStream().filter(Foo::check). 
      forEach(foo -> {status.setProperty(key, String.valueOf(a.incrementAndGet())); } 
    ); 

} 

private interface Foo { 
    public boolean check(); 
} 

Descrizione:È possibile utilizzare AtomicInteger come variabile locale in un metodo e ottenere la sicurezza dei thread?

Nell'esempio di cui sopra, lo stato è una proprietà condivise e contiene una chiave con nome COUNT. Il mio obiettivo è incrementare il conteggio e reinserirlo nelle proprietà per contare il numero di controlli eseguiti. Si consideri che il metodo tSafe viene chiamato da più thread, ottengo il conteggio corretto alla fine? Nota che ho usato AtomicInteger a come variabile locale.

+0

La sicurezza del filo non è il tipo di cosa per cui spruzzare attorno a vari costrutti multithread produce la sicurezza del filo. – Raedwald

risposta

0

Se si dispone di un solo thread, questo funzionerà, tuttavia se si dispone di più thread che lo chiamano, si hanno alcune operazioni che sono thread-safe. Questo andrà bene a condizione che ogni thread funzioni su diversi oggetti list e status.

As status è una raccolta thread-safe, è possibile bloccarla e fornito il list non viene modificato in un altro thread, questo sarebbe.

In generale, lavorare con String come numeri in modo thread-safe è molto difficile da ottenere. Sei molto più bravo a creare il tuo thread di valore, ad esempio un AtomicInteger e mai qualsiasi altra cosa.

0

No, questo non garantisce la sicurezza del filo. Anche se incrementAndGet() è esso stesso atomico, ottenere un valore dall'oggetto Properties e impostarlo non lo è.

Si consideri il seguente scenario:

  1. Discussione # 1 ottiene un valore dall'oggetto Properties. Per amor di discussione, diciamo che è "100".
  2. Il thread n. 2 ottiene un valore dall'oggetto Properties. Poiché non è successo nulla, questo valore è ancora "100".
  3. Il thread n. 1 crea un AtomicInteger, lo incrementa e inserisce "101" nell'oggetto Properties.
  4. Il thread n. 2 fa esattamente la stessa cosa e inserisce "101" nell'oggetto Properties, anziché 102 come previsto.

EDIT:
Su una nota più produttivo, un approccio migliore potrebbe essere quella di memorizzare solo il AtomicIntegersulla mappa dello stato, e incrementarlo inplace. In questo modo, hai una singola istanza e non devi preoccuparti delle gare come descritto sopra. Come la classe Properties estende Hashtable<Object, Object> questo dovrebbe funzionare tecnicamente, anche se Properties in realtà non è destinato a valori che non sono String s, e si sarebbe molto meglio con un filo moderno, sicuro Map realizzazione, come ad esempio un ConcurrentHashMap:

public void tSafe(List<Foo> list, ConcurrentMap<String, AtomicInteger> status) { 
    if(list == null) { 
     return; 
    } 
    String key = "COUNT"; 
    status.putIfAbsent(key, new AtomicInteger(0));  
    list.parallelStream() 
     .filter(Foo::check) 
     .forEach(foo -> { status.get(ket).incrementAndGet(); }); 
} 
+0

Quindi .. Devo rendere AtomicInteger una variabile finale statica, invece di usarla come variabile locale? Quali sono le migliori pratiche per utilizzare Atomic * per ottenere la sicurezza del thread? –

+0

@aravindpogu Ho provato a descrivere un approccio migliore (IMHO, ovviamente) - vedi la mia risposta modificata. – Mureinik

+0

[putIfAbsent] (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html#putIfAbsent (K,% 20V)) richiede la chiave come primo argomento, quindi arriva il valore: 'stato.putIfAbsent (ket, new AtomicInteger (0)); ' – Ferrybig

Problemi correlati