在不直接使用同步的情况下避免Java中的丢失更新

时间:2009-05-19 00:41:15

标签: java synchronization

我想知道是否有可能避免丢失更新问题,其中多个线程正在更新相同的日期,同时避免使用synchronized(x) { }

我会做很多补充和增量:

val++;
ary[x] += y;
ary[z]++;

我不知道Java如何将这些编译成字节代码,如果一个线程可以在其中一个语句字节代码块的中间被中断。换句话说那些语句线程安全吗?

另外,我知道Vector类是同步的,但我不确定这意味着什么。以下代码是否是线程安全的,因为位置i的值不会在vec.get(i)vec.set(...)之间发生变化。

class myClass {
  Vector<Integer> vec = new Vector<>(Integer);

  public void someMethod() {
    for (int i=0; i < vec.size(); i++)
      vec.set(i, vec.get(i) + value);
  }
}

提前致谢。

2 个答案:

答案 0 :(得分:6)

出于线程化的目的,+++=被视为两个操作(doublelong四个)。所以更新可以互相破坏。不只是一个,但是在错误的时刻采取行动的调度程序可能会消除毫秒的更新。

java.util.concurrent.atomic是你的朋友。

您的代码可以安全,假设您不介意每个元素单独更新而您不更改大小(!),如下所示:

for (int i=0; i < vec.size(); i++) {
    synchronized (vec) {
        vec.set(i, vec.get(i) + value);
    }
}

如果您要向Vector添加调整大小,则需要在synchronized循环之外移动for语句,您也可以使用普通新ArrayList 1}}。对于同步列表实际上没有太多用途。

但您可以使用AtomicIntegerArray

private final AtomicIntegerArray ints = new AtomicIntegerArray(KNOWN_SIZE);
[...]
    int len = ints.length();
    for (int i=0; i<len; ++i) {
        ints.addAndGet(i, value);
    }
}

这有没有锁(!)和没有拳击的优势。实现也很有趣,你需要了解它做更复杂的更新(例如随机数生成器)。

答案 1 :(得分:1)

vec.set()和vec.get()是线程安全的,因为它们不会以丢失集合和获取其他线程的方式设置和检索值。 意味着您的设置和获取将在没有中断的情况下发生。

如果您真的要像上面的示例那样编写代码,那么您应该锁定一些东西。 synchronized(vec) { }和任何人一样好。你在这里要求两个操作同步进行,而不仅仅是一个线程安全操作。

即使java.util.concurrent.atomic也只能确保一个操作(get或set)安全地发生。您需要在一次操作中获得并增加。