线程安全 - 将 final 的本地方法变量传递给线程?

8
以下代码如果'commonSet'变量是类级别字段,是否会导致相同的问题。如果它是类级别字段,我将不得不在添加到集合操作中包装一个同步块,因为HashSet不是线程安全的。由于多个线程正在添加到该集合,甚至当前线程可能会继续改变该集合,因此我应该在以下代码中执行相同的操作吗?请注意,保留HTML标签。
public void threadCreatorFunction(final String[] args) {
    final Set<String> commonSet = new HashSet<String>();

    final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                commonSet.add(newValue());
            }
        }
    };

    new Thread(runnable, "T_A").start();
    new Thread(runnable, "T_B").start();
}

对于'commonSet'的引用使用了final进行了'锁定'。但是多个线程操作它仍然可能会破坏集合中的值(它可能包含重复项?)。其次,混淆在于'commonSet'是一个方法级别变量 - 它的相同引用将在调用方法(threadCreatorFunction)的堆栈内存和run方法的堆栈内存上 - 这是正确的吗?
与此相关的问题有很多:
- 为什么传递给runnable的变量需要是final的? - 为什么只有final变量可以在匿名类中访问? 但是,我没有看到他们强调这种共享/传递可变对象的线程安全性。

请参见https://dev59.com/2HM_5IYBdhLWcg3wn0lO。 - nos
6个回答

9

不,这绝对不是线程安全的。仅仅因为将它放在了一个final变量中,这意味着两个线程会看到同样的引用,这是没问题的,但这并不使对象本身更加线程安全。

你需要同步访问或者使用ConcurrentSkipListSet


5

一个有趣的例子。

引用commonSet是线程安全且不可变的。它在第一个线程的堆栈上,并且也是您匿名Runnable类的字段。(您可以在调试器中看到这一点)

commonSet引用的集合是可变的且不是线程安全的。您需要使用synchronized或Lock使其线程安全。(或者使用线程安全的集合)


1

我认为你的第一句话缺少一个单词:

如果该方法的变量 'commonSet' 是 ??? 而不是类级别的字段,以下代码是否会导致相同的问题。

我认为你有点困惑。并发问题与可变数据结构的引用是否声明为 final 无关。你需要将引用声明为 final ,因为在 Runnable 的匿名内部类声明中,你要 将其包含进去。如果你确实要在多个线程中读写数据结构,那么你需要使用锁(同步)或使用并发数据结构,如 java.util.concurrent.ConcurrentHashMap


1
commonSet被两个线程共享。您已将其声明为final,因此使引用不可变(无法重新分配),但集合内部的实际数据仍然是可变的。假设一个线程放入一些数据,另一个线程读取一些数据。每当第一个线程放入数据时,您很可能希望锁定该集合,以便没有其他线程可以读取,直到写入该数据。这在HashSet中发生了吗?并不完全如此。

0

正如其他人已经评论的那样,您误解了一些概念,比如final和synchronized。

我认为如果您解释一下您想要用代码实现什么,那么帮助您就会更容易。我有这样的印象,这段代码片段更像是一个示例而不是实际的代码。

一些问题:为什么set在函数内部定义?它应该在线程之间共享吗?让我感到困惑的是,您使用相同的可运行实例创建了两个线程。

    new Thread(runnable, "T_A").start();
    new Thread(runnable, "T_B").start();

0
无论是单线程还是多线程使用commonset,只有引用是不可变的最终对象(即一旦分配了引用,就不能再次分配另一个对象引用),但仍然可以使用该引用修改此对象所引用的内容。
如果它不是final的,一个线程可能会再次初始化它并更改引用 commonSet = new HashSet<String>(); commonSet.add(newValue()); 在这种情况下,这两个线程可能会使用两个不同的commonsets,这可能不是你想要的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接