简体   繁体   English

集<String>线程安全

[英]HastSet<String> thread safe

===update==== ===更新====

from comment来自评论

so I clearly read the doc and know it's not thread safe and I wanted to run a small experiment to see how it will break.所以我清楚地阅读了文档并且知道它不是线程安全的,我想运行一个小实验来看看它会如何崩溃。 So the doc says the result is non deterministic.所以文档说结果是不确定的。 Does anyone know what could happen?有谁知道会发生什么? If I want to prove it's not thread safe how can I write a sample code so that I can actually see that it's no thread safe?如果我想证明它不是线程安全的,我该如何编写示例代码以便我实际上可以看到它不是线程安全的? Have you guys actually tried and seen not working example?你们真的尝试过并看到不起作用的例子吗? Do you have sample code?你有示例代码吗?

If I have three threads accessing the hashset of string.如果我有三个线程访问字符串的哈希集。

  • One adding a new string一个添加一个新字符串
  • Second removing the string第二次删除字符串
  • Third removing all第三次全部删除

Is the HashSet 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) {

    }
}

I have this test code and ran it and it seems working.我有这个测试代码并运行它,它似乎工作。 No exceptions were thrown.没有抛出异常。 I was little confused because HashSet is not thread-safe.我有点困惑,因为 HashSet 不是线程安全的。 Whay am I missing?为什么我失踪了?

From comment :来自评论

so I clearly read the doc and know it's not thread safe and I wanted to run a small experiment to see how it will break.所以我清楚地阅读了文档并且知道它不是线程安全的,我想运行一个小实验来看看它会如何崩溃。 So the doc says the result is non deterministic.所以文档说结果是不确定的。 does anyone know what could happen?有谁知道会发生什么? If I want to prove it's not thread how cam I write a sample code so that I can actually see that it's no thread safe?如果我想证明它不是线程,我如何编写示例代码以便我实际上可以看到它不是线程安全的? Have you guys actually tried and seen that not working example?你们真的尝试过并看到那个不起作用的例子吗? do you have sample code?你有示例代码吗?

The problem is that updating the Set may not be an atomic operation, especially not when the internal hash table needs to be re-sized.问题是更新Set可能不是原子操作,尤其是当内部哈希表需要重新调整大小时。

If two threads are updating at the same time, you may get the simple result that one thread overrides the change by the other thread, so you lose a change.如果两个线程同时更新,您可能会得到一个简单的结果,即一个线程覆盖另一个线程所做的更改,因此您丢失了一个更改。 More seriously, the conflict may corrupt the internal structure of the Set .更严重的是,冲突可能会破坏Set的内部结构。

To show this, here is a small program that causes high conflict during add of values.为了说明这一点,这里有一个小程序,它在值相加过程中引起高冲突。 All values added are distinct, so they should all be added, but you will see that size of the Set is incorrect when program is done, proving that some added values got lost.添加的所有值都是不同的,因此应该将它们全部添加,但是您会在程序完成时看到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);

Each time you run it, you will get a different result, eg每次运行它,你都会得到不同的结果,例如

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)

Or the program simply hangs!或者程序只是挂起!

Thread Safe线程安全

Thread unsafe does not mean you can not use it in multi-treads or the program will throw exceptions.线程不安全并不意味着您不能在多线程中使用它,否则程序会抛出异常。 It means you can not always get what you want when program is executed in multi threads.这意味着当程序在多线程中执行时,您不能总是得到您想要的。 See this for more.请参阅了解更多信息。

In computer programming, thread-safe describes a program portion or routine that can be called from multiple programming threads without unwanted interaction between the threads.在计算机编程中,线程安全描述了可以从多个编程线程调用的程序部分或例程,而线程之间没有不需要的交互。

And, you can not say an object is thread safe even if you get the expected experiment results.而且,即使您得到预期的实验结果,也不能说对象是线程安全的。 Because the results may vary in different environments.因为在不同的环境下结果可能会有所不同。 You should use synchronization mechanism provided by JDK.您应该使用 JDK 提供的同步机制。

HashSet哈希集

HashSet is not thread safe, this means: HashSet 不是线程安全的,这意味着:

  • If you write an object into it, this object may not be visible to other threads.如果你向其中写入一个对象,这个对象可能对其他线程不可见。
  • If you read the set from different threads at same time, they may get different results.如果您同时从不同线程读取该集合,它们可能会得到不同的结果。
  • If you call add first, then call removeAll , the objects in this set may not be removed.如果先调用add ,然后调用removeAll ,则该集合中的对象可能不会被删除。
  • ...... ......

User Andreas's example is pretty clear.用户 Andreas 的例子非常清楚。 Since HashSet is based on HashMap 's key set, you can refer this How to prove that HashMap in java is not thread-safe .由于HashSet是基于HashMap的键集,可以参考这个How to proof that HashMap in java is not thread-safe

Solution解决方案

JDK provided a thread safe version set , all operations on the set need aquire a inner monitor lock first. JDK 提供了一个线程安全的版本set ,该集上的所有操作都需要先获得一个内部监视器锁。

Set s = Collections.synchronizedSet(new HashSet(...));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM