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

时间:2012-08-06 07:41:03

标签: java multithreading concurrency thread-safety final

如果此方法的变量'commonSet'是类级别字段,则以下代码是否会导致相同的问题。如果它是类级别字段,我将不得不在同步块中包装添加设置操作,因为HashSet不是线程安全的。我应该在下面的代码中做同样的事情,因为多个线程正在添加到集合,甚至当前线程可能会继续改变集合。

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();
}

使用final,'commonSet'的引用被'锁定'。但是在其上运行的多个线程仍然可以破坏集合中的值(它可能包含重复项?)。其次,混淆是因为'commonSet'是一个方法级变量 - 它的相同引用将在调用方法的堆栈内存(threadCreatorFunction)和运行方法的堆栈内存上 - 这是正确的吗?

有很多与此相关的问题:

但是,我看不到他们强调线程安全部分这种共享/传递可变性。

6 个答案:

答案 0 :(得分:9)

不,这绝对不是线程安全的。只是因为你已经在最后一个变量中得到它,这意味着两个线程都会看到相同的引用,这很好 - 但它不会使对象任何更加线程安全。

您需要同步访问权限,或使用ConcurrentSkipListSet

答案 1 :(得分:5)

一个有趣的例子。

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

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

答案 2 :(得分:1)

我认为你在第一句话中遗漏了一句话:

  

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

我觉得你有点困惑。并发问题与是否声明对可变数据结构的引用final无关。您需要将引用声明为final,因为您在Runnable的匿名内部类声明中closing over。如果您实际上要让多个线程读/写数据结构,那么您需要使用锁(同步)或使用并发数据结构,如java.util.concurrent.ConcurrentHashMap

答案 3 :(得分:1)

commonSet在两个Thread之间共享。您已将其声明为final,因此您使引用不可变(您无法重新分配),但Set中的实际数据仍然是可变的。假设一个Thread放入一些数据,而另一些Thread读取一些数据。每当第一个线程放入数据时,您很可能想要锁定该Set,以便在写入该数据之前没有其他线程可以读取。 HashSet会发生这种情况吗?不是真的。

答案 4 :(得分:0)

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

我认为如果你用你的代码解释你想要完成什么,那么帮助你会容易得多。我的印象是,此代码段更像是实际代码的示例。

一些问题:为什么在函数内部定义了集合?它应该在线程之间共享吗?令我困惑的是你用同一个runnable实例创建两个线程

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

答案 5 :(得分:0)

无论是单线程还是多线程使用commonset,它只是对最终对象不可变的引用(即,一旦分配,你不能再分配另一个obj引用),但你仍然可以使用该引用修改此对象引用的内容

如果它不是最终的,则一个线程可以再次初始化它并更改引用 commonSet = new HashSet<String>(); commonSet.add(newValue()); 在这种情况下,这两个线程可能使用两个不同的公共集,这可能不是你想要的