对象锁定私有类成员 - 最佳实践?(JAVA)

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

前几天我问了一个类似的问题,但对回复不满意,主要是因为我提供的代码有一些人们关注的问题.

基本上,在Java中锁定私有成员的最佳做法是什么?假设每个私有字段只能单独操作而不能一起操作(比如下面我的Test类示例),你应该直接锁定每个私有字段(例1),还是应该使用每个私有字段的一般锁定对象来锁定(例2)?

示例1:直接锁定私有字段

class Test {
  private final List<Object> xList = new ArrayList<Object>();
  private final List<Object> yList = new ArrayList<Object>();

  /* xList methods */ 

  public void addToX(Object o) {
    synchronized(xList) {
      xList.add(o);
    }
  }

  public void removeFromX(Object o) {
    synchronized(xList) {
      xList.remove(o);
    }
  }

  /* yList methods */ 

  public void addToY(Object o) {
    synchronized(yList) {
      yList.add(o);
    }
  }

  public void removeFromY(Object o) {
    synchronized(yList) {
      yList.remove(o);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

示例2:每个私有字段使用锁定对象

class Test {
  private final Object xLock = new Object();
  private final Object yLock = new Object();
  private List<Object> xList = new ArrayList<Object>();
  private List<Object> yList = new ArrayList<Object>();

  /* xList methods */ 

  public void addToX(Object o) {
    synchronized(xLock) {
      xList.add(o);
    }
  }

  public void removeFromX(Object o) {
    synchronized(xLock) {
      xList.remove(o);
    }
  }

  /* yList methods */ 

  public void addToY(Object o) {
    synchronized(yLock) {
      yList.add(o);
    }
  }

  public void removeFromY(Object o) {
    synchronized(yLock) {
      yList.remove(o);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 9

我个人更喜欢第二种形式.没有其他的代码在所有可以使用该参考(除非反射,调试的API等).您无需担心列表的内部详细信息是否尝试在其上进行同步.(你在列表上调用的任何方法显然都可以访问this,因此可以同步它.)你纯粹是用它来锁定 - 所以你也分开了"我是锁"和"我"之间的关注点列表".

我发现这样可以更容易推理显示器,因为您可以轻松查看使用它的所有可能代码.

您可能希望创建一个纯粹用作监视器的单独类,使用覆盖toString()可以帮助进行诊断.它还可以使变量的目的更清晰.

不可否认,这种方法确实需要更多的内存,通常你不需要担心代码锁定this...但我个人认为分离问题而不必担心代码是否锁定自身的好处超过了效率成本.如果由于某种原因发现"浪费"的对象性能瓶颈(并且在分析了可能要同步的类中的代码之后),您总是可以选择第一个表单.

(就我个人而言,我希望Java和.NET 都没有 "每个对象都有一个相关的监视器"路径,但这是另一天的咆哮.)