コードは正しいです。両方とも作成する必要がありますが、競合状態は表示a
さs
れませんfinal
。また、取得および解放する必要のあるロックを使用するたびに、try/ finalを使用する必要があります。
s[i].acquire();
try {
a[i]++;
} finally {
s[i].release();
}
ただし、配列を更新する場合、アイテムごとに個別のロックを設定する必要はありません。主なコストはメモリの更新と他のネイティブ同期であるため、単一のロックも同様に適切です。とはいえ、実際の操作がそうでない場合は、または他のオブジェクトint ++
を使用する必要があります。Semaphore
Lock
ただし、単純な操作の場合は、次のようなもので十分です。
// make sure it is final if you are synchronizing on it
private final int[] a = new int[N];
...
public void m(int i) {
synchronized (a) {
a[i]++:
}
}
ブロッキングについて本当に心配している場合は、の配列AtomicInteger
が別の可能性ですが、プロファイラーが別の方法で指示しない限り、これでもやり過ぎのように感じます。
private final AtomicInteger[] a = new AtomicInteger[N];
...
public A(){
for(int i = 0; i < N; i++)
a[i] = new AtomicInteger(0);
}
public void m(int i) {
a[i].incrementAndGet();
}
編集:
単一のロック、ロックの配列、配列、および配列を比較する簡単な愚かなテストプログラムを作成しました。結果は次のとおりです。synchronized
synchronized
AtomicInteger
Semaphore
synchronized
int[]
10617msで
synchronized
Object[]
1827msのアレイ上
AtomicInteger
アレイ1414ms
Semaphore
アレイ3211ms
しかし、キッカーは、これが10スレッドで、それぞれが1,000万回の反復を実行していることです。確かに高速ですが、実際に何百万回もの反復を行わない限り、アプリケーションで目立ったパフォーマンスの向上は見られません。これが「時期尚早の最適化」の定義です。コードの複雑さ、バグの可能性の増加、デバッグ時間の追加、メンテナンスコストの増加などにお金を払うことになります。Knuthを引用するには:
小さな効率、たとえば約97%の時間は忘れておく必要があります。時期尚早の最適化は、すべての悪の根源です。
さて、OPがコメントで示唆しているように、それi++
は彼/彼が保護している実際の操作ではありません。増分にはるかに時間がかかる場合(つまり、ブロッキングを増やす場合)、ロックの配列が必要になります。