在Java中,同步共享静态对象的正确方法是什么?
这是一个关于在java中同步共享对象的正确方法的问题。一个警告是,我想要共享的对象必须从静态方法访问。我的问题是,如果我在一个静态字段上同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗 在我的具体示例中,我会问:调用PayloadService.getPayload()或PayloadService.setPayload()会锁定PayloadService.payload吗?或者它会锁定整个PayloadService类在Java中,同步共享静态对象的正确方法是什么?,java,multithreading,synchronization,Java,Multithreading,Synchronization,这是一个关于在java中同步共享对象的正确方法的问题。一个警告是,我想要共享的对象必须从静态方法访问。我的问题是,如果我在一个静态字段上同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗 在我的具体示例中,我会问:调用PayloadService.getPayload()或PayloadService.setPayload()会锁定PayloadService.payload吗?或者它会锁定整个PayloadService类 public class PayloadSe
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.getPayloadService(),以获取最新数据,我需要确保它们不会锁定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类的引用处于活动状态。您的代码应该如下所示:
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 ;
}
}
即使方法不是静态的,您的原始代码也不会工作。原因是您正在更改的负载实例上进行同步
更新,对约翰洛克评论的回应:
锁定整个类只是一个问题,如果您当前想要运行其他同步静态块。如果您想拥有多个独立的锁定分区,我建议您执行以下操作:
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 ;
}
}
或者,如果您需要更复杂的并发模式,请查看其中有许多预构建的类来帮助您
我的问题是,如果我在一个静态字段上同步,是否会像同步静态方法那样锁定该字段所属的类?或者,这只会锁定字段本身吗
不,它只是锁定对象本身(类属性而不是整个类)
这是正确的/可接受的方法吗
你也许可以看看包裹
我真的不喜欢在类属性上进行同步,但我想这只是测试的问题 该类中有一大部分功能未发布,这有助于确定该方法是否是线程安全的:如何从使用该类的其他部分访问
payloadto
实例
如果您提供的方法可以在另一个实例中交换有效负载
,而另一个线程正在运行使用有效负载
对象的代码,那么这不是线程安全的
例如,如果您有一个方法execute()
,该方法执行该类的主要工作并调用有效负载上的方法
,则需要确保当另一个线程忙于运行execute()
时,一个线程不能使用setter方法更改有效负载
实例
简而言之,当您拥有共享状态时,您需要同步状态上的所有读写操作
就我个人而言,我不理解这种方法,也永远不会采用这种方法——提供静态方法以允许其他线程重新配置一个类闻起来像是违反了关注点分离
这是正确的/可接受的方法吗
不,原因是您永远不应该对可能更改其值的变量/字段进行同步。也就是说,当您在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
和
易失性
volatile
关键字将使对变量的访问成为原子的,这意味着读取和分配变量不会由CPU本地寄存器优化,而是以原子方式完成
原子参考
AtomicReference是包中的一个特殊类,它允许原子访问类似于引用的变量。它与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 ;
}
公共类PayloadService扩展服务{
专用静态最终原子参考负载
=新的原子引用(新的payloadTo());
公共静态无效设置有效负载(有效负载到有效负载){
PayloadService.payload.set(有效载荷);
}
公共静态PayloadDTO getPayload(){
返回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();
}
}
公共类付费持有者{
private static AtomicReference holder=new AtomicReferences这不是等同于编写公共静态同步方法吗?是的,但我保留了同步作用域,以防他有更多不需要同步的代码。同步正在更改的实例有什么错?我尝试这种方法的全部原因是nd没有使用静态方法,因为我不想锁定整个clas