在 Java 中同步共享静态对象的正确方法是什么?
这是一个关于在java中同步共享对象的正确方法是什么的问题。需要注意的是,我想要共享的对象必须通过静态方法访问。我的问题是,如果我在静态字段上进行同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗?
在我的具体示例中,我问:调用 PayloadService.getPayload() 或 PayloadService.setPayload() 会锁定 PayloadService.payload 吗?或者它会锁定整个 PayloadService 类?
public class PayloadService extends Service {
private static PayloadDTO payload = new PayloadDTO();
public static void setPayload(PayloadDTO payload){
synchronized(PayloadService.payload){
PayloadService.payload = payload;
}
}
public static PayloadDTO getPayload() {
synchronized(PayloadService.payload){
return PayloadService.payload ;
}
}
...
这是正确/可接受的方法吗?
在我的示例中,PayloadService 是一个单独的线程,定期更新有效负载对象 - 其他线程需要随机间隔调用 PayloadService.getPayload() 来获取最新数据,并且我需要确保它们不会锁定 PayloadService 这种
根据响应,我重构为以下内容:
public class PayloadHolder {
private static PayloadHolder holder;
private static PayloadDTO payload;
private PayloadHolder(){
}
public static synchronized PayloadHolder getInstance(){
if(holder == null){
holder = new PayloadHolder();
}
return holder;
}
public static synchronized void initPayload(){
PayloadHolder.payload = new PayloadDTO();
}
public static synchronized PayloadDTO getPayload() {
return payload;
}
public static synchronized void setPayload(PayloadDTO p) {
PayloadHolder.payload = p;
}
}
public class PayloadService extends Service {
private static PayloadHolder payloadHolder = PayloadHolder.getInstance();
public static void initPayload(){
PayloadHolder.initPayload();
}
public static void setPayload(PayloadDTO payload){
PayloadHolder.setPayload(payload);
}
public static PayloadDTO getPayload() {
return PayloadHolder.getPayload();
}
...
方法合法吗?我也很好奇这样做是否更好,或者使用 Hardcoded ... 提到的 AtomicReference 方法更好? - 我在 PayloadService 上保留 PayloadHolder 的实例,只是为了在 PayloadService 运行时保持对 jvm 中有效的 PayloadHolder 类的引用。
This is a question concerning what is the proper way to synchronize a shared object in java. One caveat is that the object that I want to share must be accessed from static methods. My question is, If I synchronize on a static field, does that lock the class the field belongs to similar to the way a synchronized static method would? Or, will this only lock the field itself?
In my specific example I am asking: Will calling PayloadService.getPayload() or PayloadService.setPayload() lock PayloadService.payload? Or will it lock the entire PayloadService class?
public class PayloadService extends Service {
private static PayloadDTO payload = new PayloadDTO();
public static void setPayload(PayloadDTO payload){
synchronized(PayloadService.payload){
PayloadService.payload = payload;
}
}
public static PayloadDTO getPayload() {
synchronized(PayloadService.payload){
return PayloadService.payload ;
}
}
...
Is this a correct/acceptable approach ?
In my example the PayloadService is a separate thread, updating the payload object at regular intervals - other threads need to call PayloadService.getPayload() at random intervals to get the latest data and I need to make sure that they don't lock the PayloadService from carrying out its timer task
Based on the responses, I refactored to the following:
public class PayloadHolder {
private static PayloadHolder holder;
private static PayloadDTO payload;
private PayloadHolder(){
}
public static synchronized PayloadHolder getInstance(){
if(holder == null){
holder = new PayloadHolder();
}
return holder;
}
public static synchronized void initPayload(){
PayloadHolder.payload = new PayloadDTO();
}
public static synchronized PayloadDTO getPayload() {
return payload;
}
public static synchronized void setPayload(PayloadDTO p) {
PayloadHolder.payload = p;
}
}
public class PayloadService extends Service {
private static PayloadHolder payloadHolder = PayloadHolder.getInstance();
public static void initPayload(){
PayloadHolder.initPayload();
}
public static void setPayload(PayloadDTO payload){
PayloadHolder.setPayload(payload);
}
public static PayloadDTO getPayload() {
return PayloadHolder.getPayload();
}
...
Is this approach legitimate? I am also curious if it is better to do it this way or using the AtomicReference approach mentioned by Hardcoded ...?
- I am keeping an instance of PayloadHolder on PayloadService simply to keep a reference to the PayloadHolder class active in the jvm for as long as the PayloadService is running.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您的代码应该如下所示:
即使方法不是静态的,您的原始代码也不会工作。原因是您正在同步您要更改的有效负载实例。
更新,对 johnrock 评论的回复:
仅当您当前想要运行其他同步静态块时,锁定整个类才会成为问题。如果您想要有多个独立的锁定部分,那么我建议您执行以下操作:
或者,如果您需要更复杂的并发模式,请查看 java.util.concurrent 其中有许多预构建的类可以帮助您。
Your code should look like this:
Your original code wouldn't have worked even if the methods weren't static. The reason being is you were synchronizing on the payload instance that you were changing.
Update, a response to johnrock comment:
Locking the whole class is only a problem if you have other synchronized static blocks that you want to run currently. If you want to have multiple independent locked section then I suggest you do something like this:
Or, if you require a more complex concurrency pattern look at java.util.concurrent which has many pre-built classes to aid you.
正如其他帖子中提到的,您可以在类或显式监视器上进行同步。
如果我们假设您仅使用 sychnronize 来线程安全地获取和设置属性,则还有其他 2 种方法:
volatile
和 AtomicReference。易失性
易失性
关键字将使对变量的访问成为原子的,这意味着变量的读取和分配不会被CPU本地寄存器优化,而是以原子方式完成。AtomicReference
AtomicReference 是 java.util.concurrent.atomic 包,它允许对类似变量的引用进行原子访问。它与
易失性
非常相似,但为您提供了一些额外的原子操作,例如compareAndSet。示例:
编辑:
您的 Holder 似乎很困惑,因为您实例化类只是为了调用静态方法。尝试使用 AtomicReference 修复它:
You could, as mentioned in other posts, synchronize on the class or on an explicit monitor.
There are 2 other ways, if we assume that your are using the sychnronize only for thread-safe getting and setting of the property:
volatile
and AtomicReference.volatile
The
volatile
keyword will make access to the variable atomic, meaning that reading and assigning the variable won't be optimized by the CPUs local registers and are done atomically.AtomicReference
The AtomicReference is a special class at the java.util.concurrent.atomic package, which allows atomic access to a variable-like reference. It is very similiar to
volatile
, but gives you some additional atomic operations, like compareAndSet.Example:
Edit:
Your Holder seems quite confused, since you are instantiating classes only to call static Methods. A try to get it fixed with AtomicReference:
不,它只会锁定对象本身(类属性而不是整个类)
您可能可以看看java.util.concurrent.lock 包。
我真的不喜欢在类属性上同步,但我想这只是测试的问题。
No, it just lock in the object itself ( the class attribute not the whole class )
You could probably take a look at the java.util.concurrent.lock package.
I don't really like synchronizing on a class attribute, but I guess that's just a matter of teste.
同步另一个不改变的静态对象:
Synchronize on another static object that does not change:
不,这样做的原因是您永远不应该同步可以更改其值的变量/字段。也就是说,当您同步 PayloadService.payload 并设置新的 PayloadService.payload 时,您就违反了同步的黄金法则。
您应该在类实例上进行同步,或者创建一些任意的
private static final Object lock = new Object()
并对其进行同步。您将获得与在课堂上同步相同的效果。No, the reason for this is that you should never synchronize on an variable/field that can change its value. That is, when you synchronize on PayloadService.payload and set a new PayloadService.payload, then you are violating a golden rule of synchronization.
You should either synchronize on the class instance or create some arbitrary
private static final Object lock = new Object()
and synchronize on that. You will have the same effect as synchronizing on the class.此类中的主要功能未发布,这有助于确定此方法是否是线程安全的:如何从此类的其他部分访问
PayloadDTO
实例,其中它被使用了吗?如果您提供的方法可以在另一个线程正在运行使用
payload
对象的代码时交换另一个实例,那么这不是线程安全的。例如,如果您有一个方法
execute()
,该方法执行此类的主要工作并调用payload
上的方法,则需要确保一个线程无法更改 <当另一个线程正忙于运行execute()
时,使用 setter 方法的 code>payload 实例。简而言之,当您拥有共享状态时,您需要同步该状态的所有读取和写入操作。
就我个人而言,我不理解这种方法,也永远不会采用它——提供静态方法来允许其他线程重新配置类,这听起来像是违反了关注点分离。
There is a major part of functionality in this class which isn't posted which contributes to whether or not this approach is thread-safe: how do you access the
PayloadDTO
instance from the other parts of this class where it is used?If you are providing methods which could swap in another instance for
payload
while another thread is running code which uses thepayload
object, then this is not thread safe.For example if you have a method
execute()
which does the main work of this class and invokes methods onpayload
, you need to make sure that one thread cannot change thepayload
instance with the setter method while another thread is busy runningexecute()
.In short, when you have shared state, you need to synchronize on all read and write operations on the state.
Personally I don't understand this approach and would never take it - providing static methods to allow other threads to re-configure a class smells like a violation of separation of concerns.