3

锁定私有字段变量(而不是使用锁定对象)是否安全/可接受的做法?这样,我可以为不同的目的使用不同的锁。下面的例子:

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++;
    }
  }

或者我应该为我希望锁定的每个私人成员设置一个锁定对象?例子:

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

...

}

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

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

...

}
4

4 回答 4

7

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

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

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

于 2012-05-11T08:37:27.090 回答
6

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

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

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

编辑:

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

请记住x++,如果x是 Integer 实例,则等效于:

int temp = x.intValue();
temp++;
x = Integer.valueOf(temp);

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

于 2012-05-11T08:38:22.317 回答
0

我认为您应该为这两个字段设置两个不同的锁。您锁定对象以防止两个或多个线程同时访问同一个对象。

您还可以查看 java http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Lock.html中的 Lock 对象, 它比同步更高效,并且在 java.util.concurrent 中有一些实用程序类可以使用锁(如果需要,也是 ReadWriteLock)

于 2012-05-11T08:41:00.330 回答
0

AFAIK 您使用的锁定对象仅作为 ID。我的意思是,你可以使用任何你想要的对象。唯一重要的是“如果两个东西必须互斥,那么它们必须使用同一个锁”。

所以使用自己的 var 的方法似乎没问题。

但要记住!!

  • 我不认为你可以锁定一个原语,它必须是一个Object
  • 如果更改字段值,下一个进程将获得不同的锁!!!

所以单独的锁似乎更安全。除非您绝对确定您的字段不会更改(实际上,您应该将其声明为final)。

于 2012-05-11T09:39:38.423 回答