多线程在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类的引用处于活动状态
# 1 楼答案
正如在其他文章中提到的,您可以在类或显式监视器上进行同步
如果我们假设您使用sychnronize仅用于线程安全获取和设置属性:
volatile
和AtomicReference,那么还有两种其他方法易失性
volatile
关键字将使对变量的访问成为原子的,这意味着读取和分配变量不会被CPU本地寄存器优化,而是以原子方式完成原子参考
AtomicReference是java.util.concurrent.atomic包中的一个特殊类,它允许原子访问类似于引用的变量。它与
volatile
非常相似,但提供了一些额外的原子操作,如compareAndSet例如:
编辑:
持有者似乎很困惑,因为实例化类只是为了调用静态方法。尝试使用AtomicReference将其修复:
# 2 楼答案
在另一个不变的静态对象上同步:
# 3 楼答案
不,原因是您永远不应该对可能更改其值的变量/字段进行同步。也就是说,当您在PayloadService上同步时。并设置一个新的PayloadService。有效负载,那么您违反了同步的黄金法则
您应该在类实例上同步,或者创建一些任意的
private static final Object lock = new Object()
并在该类实例上同步。您将具有与在类上同步相同的效果# 4 楼答案
不,它只是锁定对象本身(类属性而不是整个类)
您可能可以看看java.util.concurrent.lock包
我真的不喜欢在类属性上进行同步,但我想这只是测试的问题
# 5 楼答案
该类中有一大部分功能没有发布,这有助于确定该方法是否是线程安全的:如何从使用该类的其他部分访问
PayloadDTO
实例如果您提供的方法可以在另一个线程运行使用
payload
对象的代码时在另一个实例中交换payload
,那么这不是线程安全的例如,如果您有一个方法
execute()
,它执行这个类的主要工作并调用payload
上的方法,那么您需要确保一个线程不能在另一个线程忙于运行execute()
时使用setter方法更改payload
实例简而言之,当您拥有共享状态时,您需要同步状态上的所有读写操作
就我个人而言,我不理解这种方法,也永远不会采用这种方法——提供静态方法以允许其他线程重新配置一个类闻起来像是违反了关注点分离
# 6 楼答案
您的代码应该如下所示:
即使方法不是静态的,您的原始代码也不会工作。原因是您正在更改的负载实例上进行同步
更新,对约翰洛克评论的回应: 锁定整个类只是一个问题,如果您当前想要运行其他同步静态块。如果您想拥有多个独立的锁定分区,我建议您执行以下操作:
或者,如果您需要更复杂的并发模式,请查看java.util.concurrent,它有许多预构建的类来帮助您