2015-04-30 13 views
19

Nach einer anderen Frage auf Stackoverflow, fragte (Java- Why this program not throwing concurrent Modification exception) Ich begann mit der HashMap zu experimentieren. Hier sind ein paar Zeilen Code, die ich schrieb:

import java.util.HashMap; 
import java.util.Random; 

public class Concurrency { 
    public static void putEntriesToMap(HashMap<String, String> hashMap) { 
     for (int count = 0; count < 10000; count++) { 
      hashMap.put(Integer.toString(count), Integer.toString(count)); 
      Random random = new Random(); 
      if (random.nextBoolean()) { 
       hashMap.remove(count + ""); 
      } 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     final HashMap<String, String> hashMap = new HashMap<String, String>(); 
     Thread t1 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       putEntriesToMap(hashMap); 
      } 
     }); 

     Thread t2 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       putEntriesToMap(hashMap); 
      } 
     }); 
     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 
    } 
} 

Es war einmal (ca. 1 in 20 Läufen), wenn Sie diesen Code ausführen, bekomme ich

Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.ClassCastException: java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode 
    at java.util.HashMap$TreeNode.moveRootToFront(HashMap.java:1819) 
    at java.util.HashMap$TreeNode.treeify(HashMap.java:1936) 
    at java.util.HashMap.treeifyBin(HashMap.java:771) 
    at java.util.HashMap.putVal(HashMap.java:643) 
    at java.util.HashMap.put(HashMap.java:611) 
    at Concurrency.putEntriesToMap(Concurrency.java:9) 
    at Concurrency$1.run(Concurrency.java:27) 
    at java.lang.Thread.run(Thread.java:745) 

Das aber scheint seltsam zu mir, weil es aussieht wie es ist ein interner HashMap-Fehler. Ich weiß, dass die Nebenläufigkeit wird nicht korrekt verwendet, aber es ist absichtlich getan.

Ich habe versucht, die Ausnahme zu googeln, aber ich habe fast keine Informationen gefunden.

Können Sie die gleiche Ausnahme reproduzieren?

Ich verwende Orakel jdk 1.8.0_40

EDIT:

Erstens Dank für Antworten, es ist für mich jetzt klar. Ich möchte nur darauf hinweisen, dass ich wusste, wie man das Programm durch thread-safe Vorsichtsmaßnahmen zu brechen, aber ich wusste nicht, warum speziell diese Ausnahme in der gegebenen Situation geworfen wird. Thomas hat es in den Kommentaren unten sehr gut erklärt. Es ist auch gut in der angenommenen Antwort erklärt. Danke noch einmal :).

+3

Es ist einfach Folge von "Nebenläufigkeit wird nicht korrekt verwendet" - "HashMap" benötigt externe Synchronisierung thread-safe. –

+0

Das ist möglich, wenn Sie nicht zwischen verschiedenen Threads synchronisieren, was ist die Frage hier? –

+0

Verwenden Sie stattdessen Sammlungen von gleichzeitigen Paketen: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html – Beri

Antwort

9

Ich fand auch die gleiche Ausnahme mit Ihrem Code. Ich fügte einen synchronized Modifikator auf der putEntriesToMap() Methode hinzu, und der Fehler schien aufzuhören aufzutreten. Das Problem ist, dass beide Threads die gleiche Map gleichzeitig modifizieren. Es gibt ein Objekt, das konvertiert werden muss, um den Eintrag einzufügen. Der zweite Thread behandelt jedoch ein mutiertes Objekt, das eine ClassCastException auslöst. Stellen Sie also sicher, dass keine zwei Threads gleichzeitig auf dieselbe Map zugreifen. Der Modifikator synchronized verhindert, dass alle anderen Threads irgendetwas mit der Klasse/Instanz tun, wenn ein anderer Thread dasselbe tut. Synchronisierte statische Methoden synchronisieren die Klasse selbst, während synchronisierte nicht-statische Methoden nur die Instanz der Klasse synchronisieren.