Java在原子操作上同步

时间:2013-07-04 08:31:23

标签: java multithreading synchronization

这个Java类线程是否安全或重置方法也需要同步?如果有,有人可以告诉我原因吗?

public class NamedCounter {
   private int count;
   public synchronized void increment() { count++; }
   public synchronized int getCount() { return count; }
   public void reset() { count = 0; }
}

5 个答案:

答案 0 :(得分:8)

不同步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的bulit-in类AtomicInteger,它涵盖了所有情况。或者,如果您正在尝试学习线程安全性,那么请使用AtomicInteger作为标准(学习)。

对于生产代码,请不要考虑使用AtomicInteger!请注意,使用AtomicInteger不会自动保证代码中的线程安全。您必须使用api提供的方法。他们是有原因的。

答案 1 :(得分:2)

请注意,synchronized不仅仅是 关于互斥,它基本上是关于操作的可见性对操作的正确排序。因此reset也必须同步,否则它所做的写操作可能会与其他两种方法同时发生,并且无法保证可见。

总而言之,您的类不是现在的线程安全,但只要您同步reset方法就会立即执行。

答案 2 :(得分:1)

您还必须同步reset()方法。

要使class线程安全,您必须同步访问变量的所有路径,否则您将获得与非同步路径不相关的结果。

答案 3 :(得分:0)

您还需要添加synchronized to reset方法,然后它将被同步。但是通过这种方式,您可以通过锁实现同步,也就是说,访问该方法的每个线程都将锁定NamedCounter对象实例。

但是,如果使用AtomicInteger作为计数变量,则不需要同步,因为它使用CAS cpu操作来实现原子性而无需同步。

答案 4 :(得分:0)

不是答案,但评论的时间太长了:

如果reset()是同步的,则0对于稍后读取或递增计数器的任何线程都可见。没有同步,就没有可见性保证。查看并发增量和非同步复位的交互,可能是0在进入方法之前对递增线程可见,那么结果将为1.如果在增量之间将counter设置为0读写,重置将被遗忘。如果在写入后设置,则结果将为0.因此,如果要为每个读取线程断言,复位后计数器为0,则该方法也必须同步。但David Schwartz认为这些低级别同步对这些交互的高级语义没有任何意义是正确的。