Java中的多个对象锁?

Xåp*_* - 3 java concurrency multithreading locking synchronized

锁定私有字段变量(而不是使用锁定对象)是安全/可接受的做法吗?这样,我可以有不同的锁用于不同的目的.示例如下:

class Test {
  private Integer x = 0;
  private Integer y = 0;

  public void incrementX() {
    synchronized(x) {
      x++;
    }
  }

  public void decrementX() {
    synchronized(x) {
      x++;
    }
  }

  public void incrementY() {
    synchronized(y) {
      y++;
    }
  }

  public void decrementY() {
    synchronized(y) {
      y++;
    }
  }
Run Code Online (Sandbox Code Playgroud)

或者我应该为每个想要锁定的私人会员拥有一个锁定对象?例:

class Test {
  private final Object xLock = new Object();
  private final Object yLock = new Object();
  private Integer x = 0;
  private Integer y = 0;

...

}
Run Code Online (Sandbox Code Playgroud)

或者我应该只有一个通用锁并将其用于需要锁定的所有私有变量?例:

class Test {
  private final Object objLock = new Object();
  private Integer x = 0;
  private Integer y = 0;

...

}
Run Code Online (Sandbox Code Playgroud)

Mar*_*nik 7

注意始终使用最终成员var来锁定!Integer例如,如果您使用a ,并且计划更改它,那将是非常糟糕的做法,因为每次调用都会看到不同的对象并导致数据竞争.

无论您使用一个还是多个锁取决于您想要实现的协调方案,因此它完全是特定于域的.您必须仔细考虑哪些操作是哪些操作,哪些操作不相互排斥,并适当地为它们分配锁定.这里没有单一的最佳实践.

如果您的对象上有两个可能同时发生的正交操作而不会导致任何datarace,那么就是两个锁的情况.在您的示例中,有两个整数,每个都独立更改.我认为这是两个锁的情况.如果你有更复杂的代码,在至少一个操作中你需要访问两个整数,那将把它们绑在一起,然后你需要一个锁.


JB *_*zet 6

只要此字段是对象,锁定私有字段就完全可以接受.基元没有内部锁,因此第一个片段无效.

但是,如果可以从外部访问此字段(例如,使用getter),我将避免锁定私有字段,因为这将允许任何人为不同目的锁定同一对象.因此,第二种解决方案是最干净的,恕我直言.

使用单个锁会适得其反,因为它会阻止对应该能够并发运行的方法的并发访问.因此,通常更好的是使用细粒度的锁.

编辑:

既然您已经更改了问题并使用了包装器对象,那么对私有Integer实例的锁定实际上并不是一个好的解决方案,因为您在方法中更改了这些变量的值.使用最终字段作为锁.

请记住x++,如果x是Integer实例,则相当于:

int temp = x.intValue();
temp++;
x = Integer.valueOf(temp);
Run Code Online (Sandbox Code Playgroud)

此外,由于Integer.valueOf()缓存了Integer实例,因此您可能有多个类使用相同的Integer实例来锁定完全不同的东西.缓慢执行和死锁的秘诀.