What is thread safety? It means that the reading and writing of data must be thread-isolated, and data loss and inconsistency cannot be caused. Data should not be overwritten every time it is modified.
Let’s take the classic example of bank withdrawal. Account A is initially 0, thread A reads 0, and then saves 100 (no data has been written yet). Thread B reads 0 and also saves 100. At this time The last account we see is a balance of 100. This is unscientific and is called thread unsafe. Therefore, we need to control the objects for deposits and withdrawals, and let the objects we operate data lock. After updating the data, other threads can achieve thread safety.
This time we will prove HashSet, we know that the Set interface is implemented. The characteristic of Set is that the data stored will not be repeated. Because it will first read the internally saved data to see if it exists. If it exists, it will not be stored in it, otherwise it will be stored in it. In other words, the data storage operation is divided into two steps, first reading, and then writing. Assuming that it is not thread-safe, a very likely situation is that when thread A determines that the set object does not have an element and is about to insert the element, thread B also determines that the object does not have the element and is also preparing to insert it. Finally, The result is that two identical elements are inserted.
We design the demo like this:
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); } } }
We test it on the main thread:
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());
Most of the printed results are the expected 5000, but occasionally there will be a situation greater than 5000 . This leads to the situation mentioned before, which proves that HashSet is not a thread-safe class.
In fact, looking at the source code, we found that HashMap is used to maintain data internally in HashSet. The fundamental reason is that HashMap is not a thread-safe class. This leads to the non-thread safety of HashSet. For more knowledge about Java collection classes, please pay attention to [PHP Chinese website: java video]
Finally, a complete code case verification:
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 } }
The above is the detailed content of Is hashset thread safe?. For more information, please follow other related articles on the PHP Chinese website!