为了尝试多线程概念,我正在实现自己的AtomicInteger版本,使用悲观锁定。它看起来像这样:
public class ThreadSafeInt {
public int i; // Should this be volatile?
public ThreadSafeInt(int i) {
this.i = i;
}
public synchronized int get() {
return i;
}
public synchronized int getAndIncrement() {
return this.i++;
}
// other synchronized methods for incrementAndGet(), etc...
}
我写了一个测试,它需要一个ThreadSafeInt实例,并将其分配给数百个线程。每个线程都会调用getAndIncrement 100,000次。我看到的是所有增量都正确发生,并且整数值恰好为
(线程数) * (每个线程的增量数)
,即使我没有在基本实例变量i上使用volatile。我本来期望如果我没有将i设置为volatile,则会出现许多可见性问题,例如线程1将i从0增加到1,但线程2仍然看到0的值并仅将其增加到1
,导致最终值小于正确值。我知道可见性问题是随机发生的,并且可能取决于环境属性,因此尽管存在固有的可见性问题,我的测试似乎可以正常工作。因此,我倾向于认为volatile关键字仍然是必要的。
但这是否正确呢?还是我的代码存在某些属性(例如仅仅是原始变量等),可以令我相信不需要使用volatile关键字呢?
i++
,通常不是原子操作,因为synchronized
被赋予了原子性。 - Kedar Mhaswadevolatile
和synchronized
之间的相似之处(和区别)。 - Gray