2016-03-15 5 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(); 
} 

Beschreibung:Können wir AtomicInteger als lokale Variable in einer Methode verwenden und Thread-Sicherheit erreichen?

In dem obigen Beispiel Status ist eine gemeinsame Eigenschaften und es enthält einen Schlüssel mit dem Namen COUNT. Mein Ziel ist es, die Anzahl zu erhöhen und sie in Eigenschaften zurückzusetzen, um die Anzahl der durchgeführten Prüfungen zu zählen. Beachten Sie, dass die tSafe-Methode von mehreren Threads aufgerufen wird. Erhalte ich am Ende die korrekte Anzahl? Beachten Sie, dass ich AtomicInteger a als lokale Variable verwendet habe.

+0

Gewindesicherheit ist nicht die Art von Sache, für die das Bestreuen verschiedener Multithreading-Konstrukte Thread-Sicherheit erzeugt. – Raedwald

Antwort

0

Wenn Sie nur einen Thread haben, funktioniert das. Wenn Sie jedoch mehr als einen Thread aufrufen, haben Sie einige Operationen, die Thread-sicher sind. Dies wird in Ordnung sein, vorausgesetzt, dass jeder Thread auf verschiedenen list und status Objekten arbeitet.

Da status eine Thread-sichere Sammlung ist, können Sie sie sperren, und vorausgesetzt, die list wird nicht in einem anderen Thread geändert, dies würde.

Im Allgemeinen ist die Arbeit mit String als Zahlen in einer Thread-sicheren Weise sehr schwierig, um richtig zu machen. Sie sind weit besser dran, wenn Sie Ihren Value-Thread erstellen, also einen AtomicInteger und sonst nichts.

0

Nein, dies garantiert nicht die Sicherheit des Fadens. Auch wenn incrementAndGet() selbst atomar ist, wird ein Wert vom Objekt Properties nicht abgerufen und zurückgesetzt.

sich das folgende Szenario:

  1. Thread # 1 einen Wert aus dem Properties Objekt erhält. Um der Sache willen sagen wir, es ist "100".
  2. Thread # 2 erhält einen Wert aus dem Objekt Properties. Da nichts passiert ist, ist dieser Wert immer noch "100".
  3. Thread # 1 erstellt ein AtomicInteger, erhöht es und platziert "101" in das Objekt Properties.
  4. Thread # 2 tut genau das gleiche und setzt "101" in das Objekt Properties statt der 102, die Sie erwartet haben.

EDIT:
Auf einer produktiveren Note, ein besserer Ansatz wäre, nur zu speichern die AtomicIntegerauf Ihren Status Karte, und es inplace zu erhöhen. Auf diese Weise haben Sie eine einzige Instanz und müssen sich nicht um Rennen wie oben beschrieben kümmern. Da die Properties Klasse Hashtable<Object, Object> erstreckt sollte dies technisch funktionieren, obwohl Properties wirklich nicht für Werte bestimmt ist, die nicht String s sind, und Sie würden viel besser dran mit einer modernen Thread-sicher Map Implementierung, wie ein ConcurrentHashMap sein:

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

Also .. Muss ich den AtomicInteger als eine statische endgültige Variable machen, anstatt es als lokale Variable verwenden? Welche Best Practices verwenden Sie, um mit Atomic * Gewindesicherheit zu erreichen? –

+0

@aravindpogu Ich habe versucht, einen besseren Ansatz zu beschreiben (IMHO, natürlich) - siehe meine bearbeitete Antwort. – Mureinik

+0

[putIfAbsent] (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html#putIfAbsent (K,% 20V)) benötigt dann den Schlüssel als erstes Argument kommt der Wert: 'Status.putIfAbsent (keet, new AtomicInteger (0)); ' – Ferrybig

Verwandte Themen