3

I asked a similar question the other day but wasn't satisfied with the response, mainly because the code I supplied had some issues that people focused on.

Basically, what is the best practice for locking private members in Java? Assuming each private field can only be manipulated in isolation and never together (like in my Test class example below), should you lock each private field directly (example 1), or should you use a general lock object per private field you wish to lock (example 2)?

Example 1: Lock private fields directly

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

Example 2: Use lock objects per private field

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);
    }
  }
}
4

4 に答える 4

9

個人的には2番目の形式が好きです。他のコードその参照を使用できません(リフレクション、デバッグAPIなどを除く)。リストの内部の詳細がリストと同期しようとしているかどうかを心配する必要はありません。(リストで呼び出すメソッドはすべて、明らかにアクセスできるためthis、同期することができます。)純粋にロックに使用しているため、「私はロックです」と「私」の間の関心の分離もあります。 mリスト」。

そうすれば、モニターを使用する可能性のあるすべてのコードを簡単に確認できるため、モニターについて推論する方が簡単だと思います。

toString()純粋にモニターとして使用するための別のクラスを作成し、診断に役立つオーバーライドを使用することをお勧めします。また、変数の目的が明確になります。

確かに、このアプローチより多くのメモリを必要とし、通常はコードのロックを心配する必要はありません...しかし、個人的には、懸念を分離し、そのコードそれ自体をロックするthisかどうかを心配する必要がないことの利点が、効率コスト。「無駄な」オブジェクトが何らかの理由でパフォーマンスのボトルネックであることがわかった場合(そして、同期する可能性のあるクラスのコードを分析した後)、いつでも最初のフォームを選択できます。

(個人的には、Javaと.NETの両方が「すべてのオブジェクトにモニターが関連付けられている」ルートをたどっていないことを望みますが、それは別の日の暴言です。)

于 2012-05-13T11:39:55.933 に答える
2

Example 1 is much better. Since xList are final, they are great for synchronization. There is no need for extra lock objects, unnecessarily complicating code and consuming memory. Only make sure the list itself is never exposed to the outside world breaking encapsulation and thread safety.

However consider:

于 2012-05-13T11:36:48.133 に答える
0

Let's put it this way: the second approach uses more code -- what does that extra code buy you? As far as concurrency, the two are exactly the same, so it must be some other aspect from the bigger picture of your app design.

于 2012-05-13T11:59:30.970 に答える
0

Even if you're sure the object you are doing the lock will never change I find it more reassuring to use special object just for locking. It makes it more transparent. If the class was to be significantly expanded and/or modified in the future by someone else he might find a reason to make xList non-final without noticing that it's used for locking. This could quickly lead to problems. Thread safety is not trivial and can grow more complex when code evolves so make it as clear and as safe as possible. Cost of having a separate object just for locking is small compared to the cost of diagnosing problems with thread-safety.

于 2012-05-14T12:13:47.930 に答える