有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

这是一个关于在java中同步共享对象的正确方法的问题。一个警告是,我想要共享的对象必须从静态方法访问。我的问题是,如果我在一个静态字段上同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗

在我的具体示例中,我会问:Will调用PayloadService。getPayload()或PayloadService。setPayload()锁定PayloadService。有效载荷?或者它会锁定整个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对象——其他线程需要调用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…)中提到的原子参考方法更好。。。? -我在PayloadService上保留一个PayloadHolder实例,只是为了在PayloadService运行期间保持对jvm中PayloadHolder类的引用处于活动状态


共 (6) 个答案

  1. # 1 楼答案

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

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

    易失性

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

    原子参考

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

    编辑:

    持有者似乎很困惑,因为实例化类只是为了调用静态方法。尝试使用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();      
      }
    }
    
  2. # 2 楼答案

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

    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 ;
        }
    }
    
  3. # 3 楼答案

    Is this a correct/acceptable approach ?

    不,原因是您永远不应该对可能更改其值的变量/字段进行同步。也就是说,当您在PayloadService上同步时。并设置一个新的PayloadService。有效负载,那么您违反了同步的黄金法则

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

  4. # 4 楼答案

    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?

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

    Is this a correct/acceptable approach ?

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

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

  5. # 5 楼答案

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

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

    例如,如果您有一个方法execute(),它执行这个类的主要工作并调用payload上的方法,那么您需要确保一个线程不能在另一个线程忙于运行execute()时使用setter方法更改payload实例

    简而言之,当您拥有共享状态时,您需要同步状态上的所有读写操作

    就我个人而言,我不理解这种方法,也永远不会采用这种方法——提供静态方法以允许其他线程重新配置一个类闻起来像是违反了关注点分离

  6. # 6 楼答案

    您的代码应该如下所示:

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

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

    更新,对约翰洛克评论的回应: 锁定整个类只是一个问题,如果您当前想要运行其他同步静态块。如果您想拥有多个独立的锁定分区,我建议您执行以下操作:

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