繁体   English   中英

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

[英]What is the correct way to synchronize a shared, static object in 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实例,只是为了在运行PaypayService的过程中保持对jvm中有效的PayloadHolder类的引用。

您的代码应如下所示:

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 ,它具有许多预构建的类来帮助您。

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

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

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

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

我真的不喜欢在class属性上进行同步,但是我想这只是teste的问题。

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

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

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

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

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 ;
    }
}

如其他文章所述,您可以在类或显式监视器上进行同步。

还有其他两种方法,如果我们假设您仅将sychnronize用于线程安全获取和设置属性,则为volatileAtomicReference

挥发物

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

的AtomicReference

AtomicReference是java.util.concurrent.atomic包中的一个特殊类,它允许原子访问类似变量的引用。 它与volatile非常相似,但是为您提供了一些其他原子操作,例如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();      
  }
}

此类中的大部分功能都未发布,这有助于此方法是否是线程安全的:如何从此类使用该类的其他部分访问PayloadDTO实例?

如果您提供的方法可以在另一个实例中交换payload而另一个线程正在运行使用payload对象的代码,则这不是线程安全的。

例如,如果您有一个方法execute()来完成此类的主要工作并在payload上调用方法,则需要确保一个线程无法在另一线程忙于运行execute()同时使用setter方法更改payload实例。 。

简而言之,当您拥有共享状态时,您需要对状态上的所有读取和写入操作进行同步。

我个人不了解这种方法,也永远不会采用这种方法-提供静态方法来允许其他线程重新配置类的味道,就像违反关注点分离。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM