首先,您的枚举中不需要嵌套类。您只需要在枚举本身中定义成员和方法,即
enum Blah {
INSTANCE;
private int someField;
public int getSomeField() { return someField; }
}
现在您可以通过以下方式访问您的单例方法:
int someField = Blah.INSTANCE.getSomeField();
此外,使成员静态在这里是一种反模式,因为单例实例应该拥有其成员。所以它们应该是实例变量,而不是静态变量。单例只有一个实例这一事实确保了 JVM 中每个成员只有一个实例。
就线程安全而言,我个人更喜欢原子变量而不是易失性变量,例如,
private final AtomicInteger count = new AtomicInteger();
private final AtomicReference<Date> date = new AtomicReference<>(new Date());
请注意,他们must被宣布final
为了真正的线程安全,因为原子变量本身不会改变,尽管它们的值可以。
如果您只需要您编码的操作,则 volatile 变量应该可以工作。与易失性变量相比,原子变量提供了更多操作,例如,compareAndSet
对于 Java 7 和getAndUpdate
and updateAndGet
对于 Java 8。请参阅this https://stackoverflow.com/questions/9749746/what-is-the-difference-of-atomic-volatile-synchronize进行讨论。
但是,如果您的成员变量是线程安全的,则可以声明它们(原子/易失性)and它们的线程安全策略是独立的,您无需担心单例中方法的线程安全性。例如,如果您需要一次原子地更新两个变量,那么您将不得不稍微重新考虑设计并引入适当的锁(两者都在设置时)and获得它们的价值)。
非常重要的是要非常小心修改您的内容的方式Date
目的。Date
is not线程安全,因此我强烈建议在进行更改时返回副本并用副本替换实例,即(假设您正在使用AtomicReference
如上),
public Date getDate() { return new Date(date.get().getTime()); }
public void setDate(Date d) {
date.set(new Date(d.getTime()));
}
最后,我强烈推荐 Brian Goetz 的《Concurrency in Practice》和 Joshua Bloch 的《Effective Java》来分别了解有关并发和单例模式的更多信息。