我有一个类似于以下类的静态数组:
public class Entry {
private String sharedvariable1= "";
private String sharedvariable2= "";
private int sharedvariable3= -1;
private int mutablevariable1 = -1
private int mutablevariable2 = -2;
public Entry (String sharedvariable1,
String sharedvariable2,
int sharedvariable3) {
this.sharedvariable1 = sharedvariable1;
this.sharedvariable2 = sharedvariable2;
this.sharedvariable3 = sharedvariable 3;
}
public Entry (Entry entry) { //copy constructor.
this (entry.getSharedvariable1,
entry.getSharedvariable2,
entry.getSharedvaraible3);
}
....
/* other methods including getters and setters*/
}
在我的程序中的某个时刻,我访问该对象的一个实例,并使用上面的复制构造函数复制它。然后我改变上面两个可变变量的值。该程序在多线程环境中运行。 请注意。所有变量都在线程之前设置其初始值。只有在程序被线程化后才能复制变量。我相信它是线程安全的,因为我只是读取静态对象,而不是写入它(即使是共享变量3,虽然只读取了int和mutable),我只是对静态对象的副本进行了更改(以及正在线程内进行复制)。但是,我想确认我的想法是正确的。
有人可以评估我在做什么吗?
答案 0 :(得分:3)
它不是线程安全的。你需要包装修改共享变量的任何东西:
synchronized (this) {
this.sharedvariable1 = newValue;
}
对于setter,您可以这样做:
public synchronized void setSharedvariable1(String sharedvariable1) {
this.sharedvariable1 = sharedvariable1;
}
然后在你的复制构造函数中,你会做同样的事情:
public Entry (Entry entry) {
this();
synchronized(entry) {
this.setSharedvariable1(entry.getSharedvariable1());
this.setSharedvariable2(entry.getSharedvariable2());
this.setSharedvariable3(entry.getSharedvariable3());
}
}
这确保了如果对实例进行了修改,则复制操作将一直等到修改完成。
答案 1 :(得分:0)
它不是线程安全的,您应该在复制构造函数中进行同步。您正在从复制构造函数中的原始对象中读取三个变量中的每一个。这些操作不是原子的。因此,当您正在读取第一个值时,第三个值会被另一个线程更改。在这种情况下,您有一个处于不一致状态的“复制”对象。
答案 2 :(得分:0)
这不是线程安全的。我的意思是,对于使用相同Entry
实例的多个线程,它不会保证线程安全。
我在这里看到的问题如下:
Thread 1
开始构建Entry
实例。它不会使该实例对其他线程的访问保持隐藏。Thread 2
使用其复制构造函数访问该实例,同时它仍在构建中。考虑Entry
字段private int sharedvariable3= -1;
的初始值,结果可能是Thread 2
创建的新“已复制”实例将设置其sharedvariable3
字段到0
(java中int类字段的默认值)。
这就是问题所在。
如果它困扰你,你必须进行synchronize
读/写操作,或者处理Entry
实例发布。这意味着,不允许其他线程访问正在构建中的Entry
实例。
答案 3 :(得分:0)
我真的不明白,为什么你认为私有实例变量是共享的。通常共享字段是静态的而不是私有的 - 我建议您不要共享私有实例变量。对于线程安全性,您应该同步变量值的操作。
您可以使用synchronized关键字,但选择正确的监视器对象(我认为条目本身应该这样做)。另一种方法是使用java.util.concurrent中的一些锁实现。通常,锁提供更高的吞吐量和更好的粒度(例如,多个并行读取,但在任何给定时间只能写入一次)。
你必须考虑的另一件事是所谓的记忆障碍。看看这篇有趣的文章http://java.dzone.com/articles/java-memory-model-programer%E2%80%99s
您可以使用volatile关键字在语义之前强制执行此操作。显式同步(锁定或同步代码)也跨越内存屏障并在语义之前强制执行。
最后一条建议:你应该不惜一切代价避免共享的可变状态。同步是一种痛苦(性能和维护方面)。由于不正确的同步导致的错误非常难以检测。设计不可变性或孤立的可变性(例如演员)更好。
答案 4 :(得分:0)
答案是在规定的条件下它是线程安全的,因为我只是从静态状态的变量中读取而只是更改副本。