在 Java 中同步共享静态对象的正确方法是什么?

发布于 2024-09-05 08:58:35 字数 2062 浏览 5 评论 0原文

这是一个关于在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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

箹锭⒈辈孓 2024-09-12 08:58:35

您的代码应该如下所示:

public static  void setPayload(PayloadDTO payload){
    synchronized(PayloadService.class){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(PayloadService.class){
        return PayloadService.payload ;
    }
}

即使方法不是静态的,您的原始代码也不会工作。原因是您正在同步您要更改的有效负载实例。

更新,对 johnrock 评论的回复:
仅当您当前想要运行其他同步静态块时,锁定整个类才会成为问题。如果您想要有多个独立的锁定部分,那么我建议您执行以下操作:

public static final Object myLock = new Object();

public static  void setPayload(PayloadDTO payload){
    synchronized(myLock){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(myLock){
        return PayloadService.payload ;
    }
}

或者,如果您需要更复杂的并发模式,请查看 java.util.concurrent 其中有许多预构建的类可以帮助您。

Your code should look like this:

public static  void setPayload(PayloadDTO payload){
    synchronized(PayloadService.class){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(PayloadService.class){
        return PayloadService.payload ;
    }
}

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:

public static final Object myLock = new Object();

public static  void setPayload(PayloadDTO payload){
    synchronized(myLock){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(myLock){
        return PayloadService.payload ;
    }
}

Or, if you require a more complex concurrency pattern look at java.util.concurrent which has many pre-built classes to aid you.

巡山小妖精 2024-09-12 08:58:35

正如其他帖子中提到的,您可以在类或显式监视器上进行同步。

如果我们假设您仅使用 sychnronize 来线程安全地获取和设置属性,则还有其他 2 种方法:volatileAtomicReference

易失性

易失性关键字将使对变量的访问成为原子的,这意味着变量的读取和分配不会被CPU本地寄存器优化,而是以原子方式完成。

AtomicReference

AtomicReference 是 java.util.concurrent.atomic 包,它允许对类似变量的引用进行原子访问。它与易失性非常相似,但为您提供了一些额外的原子操作,例如compareAndSet。

示例:

public class PayloadService extends Service {   

private static final AtomicReference<PayloadDTO> payload 
          = new AtomicReference<PayloadDTO>(new PayloadDTO());

public static void setPayload(PayloadDTO payload){
    PayloadService.payload.set(payload);
}

public static PayloadDTO getPayload() {
    return PayloadService.payload.get ;
}

编辑:

您的 Holder 似乎很困惑,因为您实例化类只是为了调用静态方法。尝试使用 AtomicReference 修复它:

public class PayloadHolder {

  private static AtomicReference<PayloadHolder> holder = new AtomicReference<PayloadHolder();    

  //This should be fetched through the holder instance, so no static
  private AtomicReference<PayloadDTO> payload = new AtomicReference<PayloadDTO>();

  private PayloadHolder(){        
  }

  public static PayloadHolder getInstance(){
    PayloadHolder instance = holder.get();

    //Check if there's already an instance
    if(instance == null){

      //Try to set a new PayloadHolder - if no one set it already.
      holder.compareAndSet(null, new PayloadHolder());
      instance = holder.get();

    }
    return instance;
  }

  public void initPayload(){      
    payload.set(new PayloadDTO());

    //Alternative to prevent a second init:
    //payload.compareAndSet(null, new PayloadDTO());
  }

  public PayloadDTO getPayload() {
    return payload.get;
  }

  public void setPayload(PayloadDTO p) {
    payload.set(p);
  }

}

public class PayloadService extends Service {   

  private final PayloadHolder payloadHolder = PayloadHolder.getInstance();

  public void initPayload(){        
    payloadHolder.initPayload();        
  }

  public void setPayload(PayloadDTO payload){       
    payloadHolder.setPayload(payload);      
  }

  public PayloadDTO getPayload() {      
    return payloadHolder.getPayload();      
  }
}

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:

public class PayloadService extends Service {   

private static final AtomicReference<PayloadDTO> payload 
          = new AtomicReference<PayloadDTO>(new PayloadDTO());

public static void setPayload(PayloadDTO payload){
    PayloadService.payload.set(payload);
}

public static PayloadDTO getPayload() {
    return PayloadService.payload.get ;
}

Edit:

Your Holder seems quite confused, since you are instantiating classes only to call static Methods. A try to get it fixed with AtomicReference:

public class PayloadHolder {

  private static AtomicReference<PayloadHolder> holder = new AtomicReference<PayloadHolder();    

  //This should be fetched through the holder instance, so no static
  private AtomicReference<PayloadDTO> payload = new AtomicReference<PayloadDTO>();

  private PayloadHolder(){        
  }

  public static PayloadHolder getInstance(){
    PayloadHolder instance = holder.get();

    //Check if there's already an instance
    if(instance == null){

      //Try to set a new PayloadHolder - if no one set it already.
      holder.compareAndSet(null, new PayloadHolder());
      instance = holder.get();

    }
    return instance;
  }

  public void initPayload(){      
    payload.set(new PayloadDTO());

    //Alternative to prevent a second init:
    //payload.compareAndSet(null, new PayloadDTO());
  }

  public PayloadDTO getPayload() {
    return payload.get;
  }

  public void setPayload(PayloadDTO p) {
    payload.set(p);
  }

}

public class PayloadService extends Service {   

  private final PayloadHolder payloadHolder = PayloadHolder.getInstance();

  public void initPayload(){        
    payloadHolder.initPayload();        
  }

  public void setPayload(PayloadDTO payload){       
    payloadHolder.setPayload(payload);      
  }

  public PayloadDTO getPayload() {      
    return payloadHolder.getPayload();      
  }
}
沦落红尘 2024-09-12 08:58:35

我的问题是,如果我在静态字段上进行同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗?

不,它只会锁定对象本身(类属性而不是整个类)

这是正确/可接受的方法吗?

您可能可以看看java.util.concurrent.lock 包。

我真的不喜欢在类属性上同步,但我想这只是测试的问题。

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?

No, it just lock in the object itself ( the class attribute not the whole class )

Is this a correct/acceptable approach ?

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.

我是有多爱你 2024-09-12 08:58:35

同步另一个不改变的静态对象:

public class PayloadService extends Service {   


private static PayloadDTO payload = new PayloadDTO();

private static final Object lock = new Object();


public static  void setPayload(PayloadDTO payload){
    synchronized(lock){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(lock){
        return PayloadService.payload ;
    }
}

Synchronize on another static object that does not change:

public class PayloadService extends Service {   


private static PayloadDTO payload = new PayloadDTO();

private static final Object lock = new Object();


public static  void setPayload(PayloadDTO payload){
    synchronized(lock){
        PayloadService.payload = payload;
    }
}

public static  PayloadDTO getPayload() {
    synchronized(lock){
        return PayloadService.payload ;
    }
}
酷炫老祖宗 2024-09-12 08:58:35

这是正确/可接受的方法吗?

不,这样做的原因是您永远不应该同步可以更改其值的变量/字段。也就是说,当您同步 PayloadService.payload 并设置新的 PayloadService.payload 时,您就违反了同步的黄金法则。

您应该在类实例上进行同步,或者创建一些任意的 private static final Object lock = new Object() 并对其进行同步。您将获得与在课堂上同步相同的效果。

Is this a correct/acceptable approach ?

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.

雾里花 2024-09-12 08:58:35

此类中的主要功能未发布,这有助于确定此方法是否是线程安全的:如何从此类的其他部分访问 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 the payload 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 on payload, you need to make sure that one thread cannot change the payload instance with the setter method while another thread is busy running execute().

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文