Maison >Java >javaDidacticiel >Comment garantir la sécurité des threads d'ArrayList, HashSet et HashMap dans l'environnement multithread à haute concurrence de Java ?
Ouvrez la première ligne de commentaires dans la méthode principale Si vous l'exécutez plusieurs fois, vous verrez un message d'exception comme celui-ci : ???
C'est. une exception de modification simultanée, tout d'abord, ArrayList est définitivement dangereux pour les threads. La raison de cette exception est que lorsque le premier thread vient d'entrer dans la collection ArrayList et souhaite effectuer une opération d'ajout, un autre thread entre également pour effectuer l'ajout. opération, et le troisième thread entre également pour effectuer l'opération d'ajout. Entrez et effectuez l'opération d'obtention, ce qui entraîne l'impossibilité de synchroniser la lecture et l'écriture, et finalement explose lors de l'impression des résultats.
Solution Regardez les lignes de commentaires restantes dans le code.
package test.notsafe; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * 演示ArrayList的线程不安全问题及解决方案 */ public class ThreadDemo2 { public static void main(String[] args) { //List<String> list = new ArrayList<>(); //解决方法1:使用Vector //List<String> list = new Vector<>(); //解决方法2:Collections //List<String> list = Collections.synchronizedList(new ArrayList<>()); //解决方法3:CopyOnWriteArrayList List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(list); },String.valueOf(i)).start(); } } }
Une explication simple sur la façon dont CopyOnWriteArrayList résout le problème d'insécurité des threads : il suffit de regarder la méthode add(E e) dans le code source :
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
Cette CopyOnWriteArrayList la verrouille d'abord avant d'effectuer l'opération d'ajout, puis passe getArray() obtient le conteneur de collection ArrayList d'origine, puis appelle la méthode Arrays.copyOf pour copier le conteneur d'origine dans un nouveau conteneur, car il doit être ajouté (la longueur sera naturellement +1), puis y ajoute des éléments nouveau conteneur. Une fois l'ajout terminé, appelez la méthode setArray pour pointer la référence du conteneur d'origine vers ce nouveau conteneur. L'avantage de procéder ainsi est le suivant : ajoutez des éléments dans un nouveau conteneur, quel que soit le conteneur d'origine, et si d'autres threads veulent obtenir et lire des éléments, ils liront toujours à partir du conteneur d'origine (c'est-à-dire que plusieurs threads peuvent lire simultanément) ; et Si d'autres threads souhaitent ajouter, ils doivent attendre que les autres threads se terminent, puis pointer la référence du conteneur d'origine vers le nouveau conteneur.
Les conteneurs CopyOnWrite sont deux conteneurs différents en matière de lecture et d'écriture, et ils utilisent également l'idée de séparation de la lecture et de l'écriture.
S'il s'agit d'un nouveau HashSet ici, la même exception de modification simultanée que l'ArrayList ci-dessus peut toujours se produire. Voir les commentaires dans le code pour la solution.
package test.notsafe; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** * 演示HashSet的线程不安全问题及解决方案 */ public class ThreadDemo3 { public static void main(String[] args) { //Set<String> set = new HashSet<>(); //解决方法1:Collections //Set<String> set = Collections.synchronizedSet(new HashSet<>()); //解决方法2:CopyOnWriteArraySet Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 0; i < 20; i++) { new Thread(() -> { set.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(set); },String.valueOf(i)).start(); } } }
package test.notsafe; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * 演示HashMap的线程不安全问题及解决方案 */ public class ThreadDemo4 { public static void main(String[] args) { //Map<String,Object> map = new HashMap<>(); //解决方法1:Collections //Map<String,Object> map = Collections.synchronizedMap(new HashMap<>()); //解决方法2:ConcurrentHashMap Map<String,Object> map = new ConcurrentHashMap<>(); for (int i = 0; i < 10; i++) { String key = String.valueOf(i); new Thread(() -> { map.put(key,UUID.randomUUID().toString().substring(0,8)); System.out.println(map); },String.valueOf(i)).start(); } } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!