这个 Java 类线程安全还是重置方法也需要同步?如果是,有人可以告诉我原因吗?
public class NamedCounter {
private int count;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }
public void reset() { count = 0; }
}
这个 Java 类线程安全还是重置方法也需要同步?如果是,有人可以告诉我原因吗?
public class NamedCounter {
private int count;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }
public void reset() { count = 0; }
}
并非没有同步 rest() 并添加更多方法。您将遇到需要更多方法的情况。例如
NamedCounter counter = new NamedCounter();
counter.increment();
// at this exact time (before reaching the below line) another thread might change changed the value of counter!!!!
if(counter.getCount() == 1) {
//do something....this is not thread safe since you depeneded on a value that might have been changed by another thread
}
要解决上述问题,您需要类似的东西
NamedCounter counter = new NamedCounter();
if(counter.incrementAndGet()== 1) { //incrementAndGet() must be a synchronized method
//do something....now it is thread safe
}
相反,请使用涵盖所有情况的 Java 内置类 AtomicInteger。或者,如果您正在尝试学习线程安全,则使用 AtomicInteger 作为标准(从中学习)。
对于生产代码,使用 AtomicInteger 无需三思而后行!请注意,使用 AtomicInteger 不会自动保证代码中的线程安全。您必须使用 api 提供的方法。他们在那里是有原因的。
请注意,这synchronized
不仅仅是关于互斥,它从根本上是关于根据操作的可见性对操作进行正确排序。因此reset
也必须同步,否则它所做的写入可能会同时发生在其他两个方法中,并且无法保证可见。
总而言之,您的类不是线程安全的,但只要您同步该reset
方法,它就会是线程安全的。
您还必须同步您的reset()
方法。
为了使class
线程安全,您必须同步访问变量的所有路径,否则您将与未同步的路径产生不希望的结果。
您还需要将同步添加到重置方法,然后它将被同步。但是通过这种方式,您可以通过锁实现同步,也就是说,访问该方法的每个线程都会锁定 NamedCounter 对象实例。
但是,如果使用 AtomicInteger 作为计数变量,则不再需要同步,因为它使用 CAS cpu 操作来实现原子性而无需同步。
不是答案,但评论太长了:
如果 reset() 已同步,则 0 对稍后读取或递增计数器的任何线程都是可见的。没有同步,就没有可见性保证。查看并发增量和非同步重置的交互,可能是在进入方法之前,增量线程可见 0,然后结果将为 1。如果在增量的读取和写入之间将计数器设置为 0,则重置将被遗忘。如果在写入后设置,则最终结果将为 0。因此,如果要断言对于每个读取线程,复位后计数器为 0,则该方法也必须同步。但是 David Schwartz 是正确的,如果没有这些交互的高级语义,这些低级同步毫无意义。