2012-08-06 32 views
8

如果此方法的变量'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(); 
} 

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

有与此相关的不少问题:

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

+0

另请参阅http://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen – nos 2012-08-06 07:45:36

回答

9

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

要么你需要同步访问,要么使用ConcurrentSkipListSet

5

一个有趣的例子。

参考commonSet是线程安全和不可变的。它位于第一个线程的堆栈上,也是匿名Runnable类的一个字段。 (你可以在调试器中看到这个)

集合commonSet指的是可变的,而不是线程安全的。你需要使用synchronized,或者使用Lock来使线程安全。 (或者使用一个线程安全的集合,而不是)

1

我想你错过了一个字,你的第一句话:

请问下面的代码原因相同的问题,如果变量“commonSet”的这种方法是一个???而不是类级别字段。

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

+0

更正第一行 – haps10 2012-08-06 07:53:44

+0

啊,我错了 - 你有一个额外的词! – DaoWen 2012-08-06 07:56:01

0

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

我认为,如果你解释你想用你的代码完成什么,那么帮助你会容易得多。我觉得这段代码更像是一个实际代码的例子。

一些问题:为什么在函数内部定义了集合?它应该在线程之间共享吗?一些困惑我的是,你箱子两个线程与可运行

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

的相同实例是否commonset所使用单线程或多这仅仅是不可改变的最终对象(基准即分配一次你不能再次分配另一个obj引用),但仍然可以使用该引用修改此对象引用的内容。

如果不是最后一个线程可以再次初始化,并改变了参考 commonSet = new HashSet<String>(); commonSet.add(newValue()); 在这种情况下,这两个线程可以使用两个不同的commonsets这可能不是你想要

1

的commonSet是共享什么两个线程。您已将其声明为final,因此您使引用不可变(您不能重新分配它),但Set内的实际数据仍是可变的。假设一个线程放入一些数据,另一些线程读取一些数据。每当第一个线程放入数据时,您最可能想要锁定该Set,以便在写入数据之前不会有其他线程读取数据。这是否发生在HashSet?不是真的。