[英]HastSet<String> thread safe
===更新====
来自评论
所以我清楚地阅读了文档并且知道它不是线程安全的,我想运行一个小实验来看看它会如何崩溃。 所以文档说结果是不确定的。 有谁知道会发生什么? 如果我想证明它不是线程安全的,我该如何编写示例代码以便我实际上可以看到它不是线程安全的? 你们真的尝试过并看到不起作用的例子吗? 你有示例代码吗?
如果我有三个线程访问字符串的哈希集。
HashSet 线程安全吗?
public void test()
{
Set<String> test = new HashSet<>();
Thread t0= new Thread(new Runnable() {
@Override
public void run() {
while (true) {
boolean c = test.contains("test");
System.out.println("checking " + c);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
test.add("test");
System.out.println("adding");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (!test.isEmpty())
{
test.removeAll(test);
}
System.out.println("removing");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t0.start();
t1.start();
t2.start();
while(true) {
}
}
我有这个测试代码并运行它,它似乎工作。 没有抛出异常。 我有点困惑,因为 HashSet 不是线程安全的。 为什么我失踪了?
来自评论:
所以我清楚地阅读了文档并且知道它不是线程安全的,我想运行一个小实验来看看它会如何崩溃。 所以文档说结果是不确定的。 有谁知道会发生什么? 如果我想证明它不是线程,我如何编写示例代码以便我实际上可以看到它不是线程安全的? 你们真的尝试过并看到那个不起作用的例子吗? 你有示例代码吗?
问题是更新Set
可能不是原子操作,尤其是当内部哈希表需要重新调整大小时。
如果两个线程同时更新,您可能会得到一个简单的结果,即一个线程覆盖另一个线程所做的更改,因此您丢失了一个更改。 更严重的是,冲突可能会破坏Set
的内部结构。
为了说明这一点,这里有一个小程序,它在值相加过程中引起高冲突。 添加的所有值都是不同的,因此应该将它们全部添加,但是您会在程序完成时看到Set
大小不正确,证明一些添加的值丢失了。
final int THREAD_COUNT = 10;
final int NUMS_TO_ADD = 100000;
Set<Integer> set = new HashSet<>();
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
final int threadNo = i;
threads[i] = new Thread() {
@Override public void run() {
for (int j = 0; j < NUMS_TO_ADD; j++)
set.add(j * THREAD_COUNT + threadNo); // all distinct values
}
};
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
threads[i].join();
System.out.println("Found " + set.size() + " values, expected " + THREAD_COUNT * NUMS_TO_ADD);
每次运行它,你都会得到不同的结果,例如
Found 898070 values, expected 1000000
Found 825773 values, expected 1000000
Found 731886 values, expected 1000000
Exception in thread "Thread-7" java.lang.ClassCastException: java.base/java.util.HashMap$Node cannot be cast to java.base/java.util.HashMap$TreeNode
at java.base/java.util.HashMap$TreeNode.moveRootToFront(HashMap.java:1883)
at java.base/java.util.HashMap$TreeNode.putTreeVal(HashMap.java:2063)
at java.base/java.util.HashMap.putVal(HashMap.java:638)
at java.base/java.util.HashMap.put(HashMap.java:612)
at java.base/java.util.HashSet.add(HashSet.java:220)
at Test$1.run(Test.java:16)
或者程序只是挂起!
线程不安全并不意味着您不能在多线程中使用它,否则程序会抛出异常。 这意味着当程序在多线程中执行时,您不能总是得到您想要的。 请参阅此了解更多信息。
在计算机编程中,线程安全描述了可以从多个编程线程调用的程序部分或例程,而线程之间没有不需要的交互。
而且,即使您得到预期的实验结果,也不能说对象是线程安全的。 因为在不同的环境下结果可能会有所不同。 您应该使用 JDK 提供的同步机制。
HashSet 不是线程安全的,这意味着:
add
,然后调用removeAll
,则该集合中的对象可能不会被删除。 用户 Andreas 的例子非常清楚。 由于HashSet
是基于HashMap
的键集,可以参考这个How to proof that HashMap in java is not thread-safe 。
JDK 提供了一个线程安全的版本set
,该集上的所有操作都需要先获得一个内部监视器锁。
Set s = Collections.synchronizedSet(new HashSet(...));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.