public class DataEvent {
private static final AtomicInteger lastRevision = new AtomicInteger();
private final int revision;
private final long threadId;
private final long timestamp;
private DataEvent(int revision) {
this.revision = revision;
this.threadId = Thread.currentThread().getId();
this.timestamp = System.nanoTime();
}
public static DataEvent newInstance() {
return new DataEvent(lastRevision.incrementAndGet());
}
}
我的问题如下:
timestamp
比上一个大。final
关键字如何影响此行为?据我了解,如果所有对象字段都是final
,那么它将以某种方式使构造函数成为原子的。在所有final
字段都已初始化之前,不会发布参考。lastRevision
原子化或将newInstance
声明为synchronized
?答案 0 :(得分:2)
说所有对象都将被构造是绝对正确的 一一贯地
不。 lastRevision.incrementAndGet()
是一个阻塞调用,但是调度程序可以在接收到第一个ID之后暂停第一个线程,然后在第二个线程接收到第二个ID之后恢复它,这意味着两个构造函数将同时执行。
换句话说,每个新对象的时间戳都大于 上一个。
不,请参见上方。
final
关键字如何影响此行为?
不是。
据我了解,如果所有对象字段都是最终字段,那么 原子构造器
不正确。如果每个字段都是final,则类是不可变的*,这使其隐式具有线程安全性。
构造此类对象的最佳实践是什么?足以使 应该
lastRevision
原子还是newInstance
被声明为已同步?
如果必须依次创建每个实例,则newInstance
应该同步。一旦存在,原子整数就没有意义了。即使执行此操作,时间戳也可能仍然相同,具体取决于底层系统时钟的分辨率。
*好吧,不完全是。如果每个字段都是最终字段,并且本身也是不可变的。
答案 1 :(得分:1)
原子性保证仅对incrementAndGet()
调用有效。因此,是的,每个新对象的修订都是连续的,这是原子数据类型的目的。因此,回答您的问题:不能保证,多个线程将以与调用incrementAndGet()
相同的顺序执行构造函数中的语句。为此,您必须将此部分放在synchronized
块中。
final
在这里没有帮助。它们是纯粹的逻辑功能,不允许在对象创建后对字段进行变异。
lastRevision
不必是原子的。此外,无论您决定做什么,您可能还希望检查System.nanoTime()
本身提供的担保。 Click。