Qu'est-ce que la sécurité des threads ? Cela signifie que la lecture et l'écriture des données doivent être isolées des threads, ce qui ne doit pas entraîner de perte de données ou d'incohérence. Chaque fois que les données sont modifiées, elles ne doivent pas être écrasées.
Prenons l'exemple classique d'un retrait bancaire. Le compte A lit initialement 0, le thread A lit 0, puis enregistre 100 (aucune donnée n'a encore été écrite) et enregistre également 100. temps Le dernier compte que nous voyons est un solde de 100. Ceci n’est pas scientifique et est appelé thread dangereux. Par conséquent, nous devons contrôler les objets pour les dépôts et les retraits et laisser les objets que nous exploitons verrouiller les données après la mise à jour des données, d'autres threads peuvent assurer la sécurité des threads.
Cette fois, nous prouverons HashSet Nous savons que l'interface Set est implémentée. La caractéristique de Set est que les données stockées ne seront pas répétées. Parce qu'il lira d'abord les données enregistrées en interne pour voir si elles existent, elles n'y seront pas stockées, sinon elles y seront stockées. En d’autres termes, l’opération de stockage des données est divisée en deux étapes : d’abord la lecture, puis l’écriture. En supposant qu'il n'est pas thread-safe, une situation très probable est que lorsque le thread A détermine que l'objet défini n'a pas d'élément et est sur le point d'insérer l'élément, le thread B détermine également que l'objet n'a pas l'élément et est sur le point d'insérer l'élément. se prépare également à l'insérer. Finalement, le résultat est que deux éléments identiques sont insérés.
Nous concevons la démo comme ceci :
class TestHashSet implements Runnable{ // 实现Runnable 让该集合能被多个线程访问 Set<Integer> set = new HashSet<Integer>(); // 线程的执行就是插入5000个整数 @Override public void run() { for (int i = 0;i < 5000;i ++) { set.add(i); } } }
Nous la testons sur le fil principal :
TestHashSet run2 = new TestHashSet(); // 实例化两个线程 Thread t6 = new Thread(run2); Thread t7 = new Thread(run2); // 启动两个线程 t6.start(); t7.start(); // 当前线程等待加入到调用线程后 t6.join(); t7.join(); // 打印出集合的size System.out.println(run2.set.size());
La plupart des résultats imprimés sont les 5000 attendus, mais ils apparaissent parfois. La situation est supérieure à 5000. Cela conduit à la situation mentionnée précédemment, qui prouve que HashSet n'est pas une classe thread-safe.
En fait, en regardant le code source, j'ai découvert que HashMap est utilisé pour conserver les données en interne dans HashSet. La raison fondamentale est que HashMap n'est pas une classe thread-safe. Cela conduit à la sécurité non-thread de HashSet. Pour plus de connaissances sur les classes de collection Java, veuillez prêter attention à [Site Web PHP chinois : vidéo Java]
Enfin, une vérification complète de la casse du code :
import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * 验证HashSet不是线程安全 */ public class HashSetTest { public static void main(String[] args) { final Set<Integer> set = new HashSet<>();// 结果可能大于1000 // final Set<Integer> set = Collections.synchronizedSet(new HashSet<>());// 结果等于1000 // final Set<Integer> set = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());// 结果等于1000 // 往set写入1-1000 Runnable runnable = new Runnable() { @Override public void run() { for (int i = 1; i <= 1000; i++) { set.add(i); } } }; int threadNum = 10;// 线程数 List<Thread> threadList = new ArrayList<>(); for (int i = 0; i < threadNum; i++) { Thread thread = new Thread(runnable); threadList.add(thread); thread.start(); } // 主线程等待子线程执行完成 for (Thread thread : threadList) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(set.size());// 结果可能大于1000 } }
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!