[英]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.