2

假设我有一个类 [X](及其对象 [xObject]),其中有两个整数 [A] 和 [B]。我有两个线程一个修改[A],另一个修改[B]。现在,由于两个线程都更改了 [xObject] 的值,我应该同步访问 [xObject] 还是仅同步访问 [A] 和 [B],因为它们具有不同的内存位置(实际上)。这个 FALL 结束的地方,就像我应该同时访问数组中的两个不同位置,还是在 arrayList 中?

4

4 回答 4

1

如果您的类将在多线程环境中使用,我认为锁定使用 getter 或公共访问提供的任何数据是一个好习惯。

这样您就不会强迫用户(使用您的类作为 API 的代码)了解或考虑特定规则(“如果您正在访问 B,则不应修改 A”)等。人们可以立即使用可用的方法而不必担心需要同步哪个数据项。文档也更少;)

于 2013-08-19T11:15:51.347 回答
1

如果您有两个独立的int属性,则根本不需要同步:同步的唯一原因是在涉及多个内存位置时确保一致性。使用独立的位置volatile足以确保您的更改的跨线程可见性。

例如,如果您的int属性不是独立的,则需要同步。假设一个属性代表当前值,另一个代表最大值。设置大于最大值的当前值会引发异常;将最大值设置为大于当前值会将当前值更改为新的最大值。

public class Test {
    int current = 10;
    int max = 100;
    public synchronized int getMax() { return max; }
    public synchronized int getCurrent() { return max; }
    public synchronized void setCurrent(int c) {
        if (c > max) throw new IllegalStateException();
        current = c;
    }
    public synchronized void setMax(int m) {
        max = m;
        if (current > m) current = m;
    }
}

数组和集合的决定更复杂:如果您访问数组元素,但数组本身保持固定,并且数组元素是独立的,则可以跳过同步。但是,如果元素不是独立的,或者如果集合更复杂(例如,数组列表或哈希表),那么您需要同步,因为集合操作可能会在结构上改变集合,从而破坏一致性。

于 2013-08-19T10:55:07.297 回答
1

不确定您对数组等的含义。但是如果您更改了两个不同的值而不是在另一个线程中读取它们,则不需要同步。

如果您在线程 1 中更改变量 xObject.a 的值并在线程 2 或其他线程 3 中读取它,那么您可能会得到过时的读取,除非您在类 X 中将 a 设置为 volatile。

或者使用 java.util.concurrent 包中的同步实用程序之一。可以谷歌搜索或在stackoverflow中搜索示例。

于 2013-08-19T10:37:10.967 回答
0

这取决于客户不应该看到的中间状态。

例如,如果对象不允许 A 和 B 的任何组合,那么您应该在两个访问上进行同步:

class Person {
  private final Object _lock = new Object(); // for synchronization

  private int _age;
  private boolean _married;

  public void setAge(int age) {
    if (age <= 0) throw new IllegalArgumentException();

    synchronized(_lock) {
        // age < 18 implies !married
        _age = age;
        if (age < 18) married = false;
    }
  }
}

即使 A 和 B 彼此独立,您也可能希望这样做:

class ComplexNumber {
  private final Object _lockRe = new Object();
  private final Object _lockIm = new Object();
  private int _re, _im;

  ...

  public void setValue(int re, int im) {
    synchronized(_lockRe) { _re = re; }
    synchronized(_lockIm) { _im = im; }
  }
}

如果你这样做

ComplexNumber c = new ComplexNumber(0, 0);
c.setValue(1, 2);

然后一个线程可能会看到 (1,0),它从未设置过:这可能是可接受的,也可能不是,取决于您的应用程序。

于 2013-08-19T11:02:24.703 回答