这个Demo有2个地方可能造成数组越界
(1)BaseLine本身中的index++是线程不安全的
class BaseLine extends Accumulator { { id = "BaseLine"; } @Override public void accumulate() { value += preLoaded[index++]; if(index >= SIZE) index = 0; } @Override public long read() { return value; } }
解决办法 :
preLoaded[(index++)%SIZE];
(2)index.getAndIncrement()和index.set(0)没有同步
首先index.getAndIncrement()是线程安全的 , i+1也没什么问题 , 但是index.getAndIncrement()和index.set(0)不是同步的
class AtomicTest extends Accumulator { { id = "Atomic"; } private AtomicInteger index = new AtomicInteger(0); private AtomicLong value = new AtomicLong(0); @Override public void accumulate() { // Oops! Relying on more than one Atomic at // a time doesn't work. But it still gives us // a performance indicator: int i = index.getAndIncrement(); value.getAndAdd(preLoaded[i]); if(i+1 >= SIZE){//有可能一个线程还没设置index.set(0),另一个线程就执行了index.getAndIncrement(),造成数组越界 index.set(0); } } @Override public long read() { return value.get(); } }有2种解决办法
第1种 : 加上synchronized (不过这样做使用AtomicInteger 在accumulate()方法中就没什么意义了 , 普通的int类型也能同步 , 如果你运行的话 , 会发些比单纯使用int和synchronized快很多 , 因为AtomicInteger减少了读取的时间)
public void accumulate() { synchronized (this) { int i = index.getAndIncrement(); value.getAndAdd(preLoaded[index.getAndIncrement()]); if(i+1 >= SIZE-1){ index.set(0); } } }第2种 : 保证测试的次数不超过数组的大小(这样就不用设置index.set(0)了)
public void accumulate() { int i = index.getAndIncrement(); value.getAndAdd(preLoaded[i]); }