我正在编写一个实用程序类来实现秒表的行为。这个类的一个重要特性是它被设计为线程安全的。我使用名为private final
的{{1}}字段进行同步。该类中的两个方法称为lock
和reset
,它们分别重置秒表并启动它。它们实现为:
start
最近,我想到了一个名为public void reset() {
synchronized (lock) {
beginTime = 0;
lapIndex = 0;
}
}
public void start() {
synchronized (lock) {
if (beginTime == 0) {
beginTime = System.nanoTime();
laps[lapIndex++] = beginTime;
}
}
}
的额外便利方法,它会重置并启动秒表。我希望它的行为类似于原子操作,所以我的想法是将其实现为:
restart
但是,public void restart() {
synchronized(lock) {
reset();
start();
}
}
和reset
方法已在start
上同步,因此调用lock
会在同一对象上同步两次。可能会出现任何问题吗?是否多次同步同一对象的行为定义明确?有必要吗?我已经运行了代码,因为似乎工作正常,但我担心我可能会遗漏一些微线程,这与多线程一样。
答案 0 :(得分:3)
两次同步没问题。该线程已经拥有了监视器,因此额外的同步并没有真正做多少。这是必要的,否则您的restart()
方法可能会被调用reset()
和start()
之间的另一个线程中断。
避免双重同步的一种方法是让restart()
,reset()
和start()
同步,然后委托给非同步的内部方法。
public void reset() {
synchronized (lock) {
_reset();
}
}
public void start() {
synchronized (lock) {
_start();
}
}
public void restart() {
synchronized(lock) {
_reset();
_start();
}
}
private void _reset() {
beginTime = 0;
lapIndex = 0;
}
private void _start() {
if (beginTime == 0) {
beginTime = System.nanoTime();
laps[lapIndex++] = beginTime;
}
}
答案 1 :(得分:2)
来自Java语言规范的section 14.19 on synchronized
statements:
synchronized语句获取的锁与通过synchronized方法隐式获取的锁相同(第8.4.3.6节)。 单个帖子可能会多次获得锁定。
同样来自JLS:
线程 t 可能会多次锁定特定的监视器;每次解锁都会逆转一次锁定操作的效果。
因此,这应该不是问题。