[英]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用于线程安全获取和设置属性,则为volatile
和AtomicReference 。
挥发物
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.