2017-11-13 5 views
2

私は非常に単純なWebサービスを持っているとしましょう。唯一のタスクは、そのエンドポイントが何回呼び出されたのかを数えることです。エンドポイントは/helloです。メソッドの同時呼び出しを集計する

@Controller 
public class HelloController { 

    private int calls = 0; 

    @RequestMapping("/hello") 
    public String hello() { 
     incrementCalls(); 
     return "hello"; 
    } 

    private void incrementCalls() { 
     calls++; 
    } 
} 

今は、この限り2人のユーザーが同時に、同じ時間に/helloを呼び出さないように、すべて正常に動作します。しかし、/helloへの並列呼び出しが発生した場合、calls変数は1回だけインクリメントされます(私が間違っていない場合)。だから明らかに何らかの種類の同期がここで行われる必要があります。

このメソッドをスレッドセーフにする最良の方法は何ですか?

+2

'int'ではなく' java.util.concurrent.atomic.AtomicInteger'を使用してください。 – Jesper

+2

コールのカウントにアトミックなlong型または整数型を追加しても問題ありません。さもなければ、コールカウント変数をロックで保護します。 –

+0

関連:https://stackoverflow.com/questions/16795303/must-spring-mvc-classes-be-thread-safe – assylias

答えて

3

calls++が期待以外の動作を引き起こす原因は、アトミックではない場合があります。アトミック操作は、操作全体が別のスレッドによって傍受されないように発生します。アトミシティは、操作をロックするか、すでにアトミックに実行しているハードウェアを利用することによって実装されます。

インクリメントは、calls = calls + 1;のショートカットであるため、あなたが想定したように、アトミック操作ではない可能性が最も高いです。どちらかが増分する前に、2つのスレッドがcallsの同じ値を取得することが実際に起こる可能性があります。両方とも、すでにインクリメントされた値を取得する代わりに、同じ値を保存します。

ゲットアンドインクリメントをアトミック操作に変換する簡単な方法がいくつかあります。すべての輸入を必要としないあなたのための最も簡単な一つは、あなたの方法​​を作ることです。

private void incrementCalls() { 
    calls++; 
} 

これは、暗黙のうちに、それはスレッドがメソッドに入るたびに属しHelloControllerオブジェクトにロックします。他のスレッドは、ロックを解放してメソッドに入るまで待たなければならず、メソッド全体をアトミックな操作にする必要があります。

もう1つの方法は、必要なコードの部分を明示的に同期させることです。これは、多くの場合、同期はかなり時間とスペース高価であるため、アトミック操作の多くを持っている必要はありません大型方法のためのより良い選択である:全体の方法​​作る

private void incrementCalls() { 
    sychronized(this) { 
     calls++; 
    } 
} 

はラッピングのためだけのショートカットです全体の内容はsynchronized(this)です。

java.util.concurrent.atomic.AtomicIntegerは、整数に対して実行したい操作のほとんどをアトミックな操作にするために同期を処理します。この場合、getAndAdd(1)、またはgetAndIncrement()に電話することができます。これはおそらく、中括弧の数を減らし、慎重に設計されたライブラリ関数を使用するので、コードを読みやすいように保つという点で最もクリーンな解決策になるでしょう。

関連する問題