1

假设我有以下代码:

final Catalog catalog = createCatalog();

for (int i = 0; i< 100; i++{
    new Thread(new CatalogWorker(catalog)).start();
}

“Catalog”是一个对象结构,createCatalog() 方法和“Catalog”对象结构在编写时并没有考虑到并发性。产品目录中有几个非最终的、非易失的引用,甚至可能存在可变状态(但这必须处理)

我理解内存模型的方式,这段代码不是线程安全的。有什么简单的方法可以让它安全吗?(这个问题的广义版本实际上是关于在线程爆炸开始之前创建的共享结构的单线程构造)

4

3 回答 3

5

不,没有简单的方法可以让它安全。可变数据类型的并发使用总是很棘手。在某些情况下,使每个操作都Catalog同步(最好在私有锁上)可能有效,但通常您会发现一个线程实际上想要执行多个操作,而不会冒任何其他线程搞乱事物的风险。

只需同步对变量的每次访问就足以使 Java 内存模型问题的相关性降低——例如,您总是会看到最新的值——但更大的问题本身仍然很重要。

中的任何不可变状态都Catalog应该已经很好了:在构建Catalog和启动新线程之间存在“之前发生”。从规范的第 17.4.5 节:

线程上的 start() 调用发生在已启动线程中的任何操作之前。

(并且构造完成发生在调用之前start(),因此构造发生在启动线程中的任何操作之前。)

于 2009-07-01T06:41:05.920 回答
1

您需要同步每个更改状态的方法Catalog以使其成为线程安全的。

public synchronized <return type> method(<parameter list>){
...
}
于 2009-07-01T06:40:06.860 回答
1

假设您处理“非最终、非易失性引用 [和] 可变状态”(大概是在这些线程运行时实际上没有改变任何东西),那么我相信这是线程安全的。从JSR-133 常见问题解答

当一个动作发生在另一个动作之前时,第一个动作保证在第二个动作之前排序并且对第二个动作可见。此排序规则如下:

  • 线程中的每个动作都发生在该线程中的每个动作之前,该线程中的每个动作都按程序顺序进行。
  • 监视器上的解锁发生在同一监视器上的每个后续锁定之前。
  • 对 volatile 字段的写入发生在对同一 volatile 的每次后续读取之前。
  • 线程上的 start() 调用发生在已启动线程中的任何操作之前。
  • 线程中的所有操作都发生在任何其他线程从该线程上的 join() 成功返回之前。

由于线程是在调用 createCatalog 之后启动的,因此这些线程应该可以看到 createCatalog 的结果而不会出现任何问题。只有在线程上调用 start() 之后发生的 Catalog 对象的更改才会导致麻烦。

于 2009-07-01T06:40:57.963 回答