Threadsafe发布java对象结构?

时间:2022-06-13 21:01:49

Assuming that I have the following code:

假设我有以下代码:

final Catalog catalog = createCatalog();

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

"Catalog" is an object structure, and the method createCatalog() and the "Catalog" object structure has not been written with concurrency in mind. There are several non-final, non-volatile references within the product catalog, there may even be mutable state (but that's going to have to be handled)

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

The way I understand the memory model, this code is not thread-safe. Is there any simple way to make it safe ? (The generalized version of this problem is really about single-threaded construction of shared structures that are created before the threads explode into action)

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

3 个解决方案

#1


No, there's no simple way to make it safe. Concurrent use of mutable data types is always tricky. In some situations, making each operation on Catalog synchronized (preferably on a privately-held lock) may work, but usually you'll find that a thread actually wants to perform multiple operations without risking any other threads messing around with things.

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

Just synchronizing every access to variables should be enough to make the Java memory model problem less relevant - you would always see the most recent values, for example - but the bigger problem itself is still significant.

只是同步每次访问变量应该足以使Java内存模型问题不那么相关 - 例如,你总是会看到最新的值 - 但更大的问题本身仍然很重要。

Any immutable state in Catalog should be fine already: there's a "happens-before" between the construction of the Catalog and the new thread being started. From section 17.4.5 of the spec:

Catalog中的任何不可变状态都应该是正常的:在Catalog的构造和正在启动的新线程之间存在“发生之前”。从规范的第17.4.5节:

A call to start() on a thread happens-before any actions in the started thread.

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

(And the construction finishing happens before the call to start(), so the construction happens before any actions in the started thread.)

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

#2


You need to synchronize every method that changes the state of Catalog to make it thread-safe.

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

public synchronized <return type> method(<parameter list>){
...
}

#3


Assuming you handle the "non-final, non-volatile references [and] mutable state" (presumably by not actually mutating anything while these threads are running) then I believe this is thread-safe. From the JSR-133 FAQ:

假设你处理“非最终的,非易失性的引用[和]可变状态”(可能是因为在这些线程运行时实际上没有改变任何东西),那么我相信这是线程安全的。来自JSR-133 FAQ:

When one action happens before another, the first is guaranteed to be ordered before and visible to the second. The rules of this ordering are as follows:

当一个动作发生在另一个动作之前时,第一个动作保证在第二个动作之前被命令并且可见。这种排序的规则如下:

  • Each action in a thread happens before every action in that thread that comes later in the program's order.
  • 线程中的每个操作都发生在该线程中的每个操作之前,该操作在程序的顺序中稍后出现。

  • An unlock on a monitor happens before every subsequent lock on that same monitor.
  • 监视器上的解锁发生在同一监视器上的每个后续锁定之前。

  • A write to a volatile field happens before every subsequent read of that same volatile.
  • 对易失性字段的写入在每次后续读取相同的易失性之前发生。

  • A call to start() on a thread happens before any actions in the started thread.
  • 在线程上调用start()发生在启动线程中的任何操作之前。

  • All actions in a thread happen before any other thread successfully returns from a join() on that thread.
  • 线程中的所有操作都发生在任何其他线程从该线程上的join()成功返回之前。

Since the threads are started after the call to createCatalog, the result of createCatalog should be visible to those threads without any problems. It's only changes to the Catalog objects that occur after start() is called on the thread that would cause trouble.

由于线程是在调用createCatalog之后启动的,因此createCatalog的结果对于那些线程应该是可见的而没有任何问题。它只对在线程上调用start()之后发生的Catalog对象的更改才会导致问题。

#1


No, there's no simple way to make it safe. Concurrent use of mutable data types is always tricky. In some situations, making each operation on Catalog synchronized (preferably on a privately-held lock) may work, but usually you'll find that a thread actually wants to perform multiple operations without risking any other threads messing around with things.

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

Just synchronizing every access to variables should be enough to make the Java memory model problem less relevant - you would always see the most recent values, for example - but the bigger problem itself is still significant.

只是同步每次访问变量应该足以使Java内存模型问题不那么相关 - 例如,你总是会看到最新的值 - 但更大的问题本身仍然很重要。

Any immutable state in Catalog should be fine already: there's a "happens-before" between the construction of the Catalog and the new thread being started. From section 17.4.5 of the spec:

Catalog中的任何不可变状态都应该是正常的:在Catalog的构造和正在启动的新线程之间存在“发生之前”。从规范的第17.4.5节:

A call to start() on a thread happens-before any actions in the started thread.

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

(And the construction finishing happens before the call to start(), so the construction happens before any actions in the started thread.)

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

#2


You need to synchronize every method that changes the state of Catalog to make it thread-safe.

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

public synchronized <return type> method(<parameter list>){
...
}

#3


Assuming you handle the "non-final, non-volatile references [and] mutable state" (presumably by not actually mutating anything while these threads are running) then I believe this is thread-safe. From the JSR-133 FAQ:

假设你处理“非最终的,非易失性的引用[和]可变状态”(可能是因为在这些线程运行时实际上没有改变任何东西),那么我相信这是线程安全的。来自JSR-133 FAQ:

When one action happens before another, the first is guaranteed to be ordered before and visible to the second. The rules of this ordering are as follows:

当一个动作发生在另一个动作之前时,第一个动作保证在第二个动作之前被命令并且可见。这种排序的规则如下:

  • Each action in a thread happens before every action in that thread that comes later in the program's order.
  • 线程中的每个操作都发生在该线程中的每个操作之前,该操作在程序的顺序中稍后出现。

  • An unlock on a monitor happens before every subsequent lock on that same monitor.
  • 监视器上的解锁发生在同一监视器上的每个后续锁定之前。

  • A write to a volatile field happens before every subsequent read of that same volatile.
  • 对易失性字段的写入在每次后续读取相同的易失性之前发生。

  • A call to start() on a thread happens before any actions in the started thread.
  • 在线程上调用start()发生在启动线程中的任何操作之前。

  • All actions in a thread happen before any other thread successfully returns from a join() on that thread.
  • 线程中的所有操作都发生在任何其他线程从该线程上的join()成功返回之前。

Since the threads are started after the call to createCatalog, the result of createCatalog should be visible to those threads without any problems. It's only changes to the Catalog objects that occur after start() is called on the thread that would cause trouble.

由于线程是在调用createCatalog之后启动的,因此createCatalog的结果对于那些线程应该是可见的而没有任何问题。它只对在线程上调用start()之后发生的Catalog对象的更改才会导致问题。