AtomicIntegerの性能を検証する

# マルチスレッドでintを使う
そもそもJavaやAndroidでintを使う場合，複数のスレッドから演算をするときはロックを取らないといけません．これは1行の演算やインクリメント・デクリメントでも同じです．
例えば`i = i + 1`を演算する場合，まず`i`を取得して，1を足してからまた`i`に代入しますが，取得してから代入するまでに他のスレッドが`i`の値を変更している可能性があります．
`i++`と書いた場合も同様でインクリメントしている間に値が変わっている可能性があります．
アセンブラとかに詳しい人は「インクリメントなんて機械語でアトミックな処理があるじゃん」と思うかもしれませんが，`i+=2`の場合はロックを取らないといけないことを考えると，言語仕様として一貫性を保つためにも`i++`はアトミックな処理になっていないのではないか，と想像します．

## そもそも本当に値は変わってるのか
「とはいってもそんなの滅多に起きないでしょ？」と思う気持ちはもっともなので試してみましょう．

```java
public class AITest {
    public int i;

    public AITest(){
        i = 0;
    }

    public static void main(String[] args) throws Throwable {
        test1();
    }

    public static void test1() throws Throwable {
        int threadNum = 20;
        AITest test = new AITest();
        ExecutorService service = Executors.newFixedThreadPool(threadNum);
        long start = System.currentTimeMillis();
        for (int n = 0; n < threadNum; n++) {
            service.submit(()->{
                for (int m = 0; m < 100000; m++) {
                    test.i++;
                }
            });
        }
        service.shutdown();
        service.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println(test.i+" : "+(System.currentTimeMillis() - start));
    }
}
```

一応解説しておくと
1.`Executors.newFixedThreadPool(threadNum)`で固定数のスレッドプールを作る
2.実行時間を計るために現在時刻の取得
3.スレッド数分のループを作って，各スレッドにタスクを投入する
4.各スレッドは10万回インクリメントする
5.`service.shutdown()`と`service.awaitTermination()`を使って終了まで待機
6.値の出力（成功していれば20*100000=2000000になるはず）

さて，実行結果は

```
1264434 : 77
```

ということで盛大にズレます．ちなみに複数回行うと毎回違う値が出力されます．あと`volatile`を付けても関係ありません．`volatile`は取得するときに最新の値が取れる，ぐらいに思っておけばいいです．
ついでに実行時間は77msecです．もっと早い環境で試験するなら`System.nanoTime()`を使ってください．

# synchronizedを使ってロックする
もしかしたら一番一般的（？）なsynchronizedによる実装は下記の通り．testメソッドだけ載せます．

```java
    public static void test2() throws Throwable {
        int threadNum = 20;
        AITest test = new AITest();
        ExecutorService service = Executors.newFixedThreadPool(threadNum);
        long start = System.currentTimeMillis();
        for (int n = 0; n < threadNum; n++) {
            service.submit(()->{
                for (int m = 0; m < 100000; m++) {
                    synchronized (test){
                        test.i++;
                    }
                }
            });
        }
        service.shutdown();
        service.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println(test.i+" : "+(System.currentTimeMillis() - start));
    }
```

AITestクラスに`synchronized public void increment(){}`みたいなメソッドを追加しても同じです．で，結果は

```
2000000 : 171
```

ということで正確な値が出ました．かかった時間は171msecということでやはりロック取得に時間がかかってます．

# AtomicInteger#incrementAndGet()を使う
ようやく本番です．

```java
public class AITest {
    public final AtomicInteger ai;
    public AITest(){
        ai = new AtomicInteger(0);
    }

    public static void test3() throws Throwable {
        int threadNum = 20;
        AITest test = new AITest();
        ExecutorService service = Executors.newFixedThreadPool(threadNum);
        long start = System.currentTimeMillis();
        for (int n = 0; n < threadNum; n++) {
            service.submit(()->{
                for (int m = 0; m < 100000; m++) {
                    test.ai.incrementAndGet();
                }
            });
        }
        service.shutdown();
        service.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println(test.ai.get()+" : "+(System.currentTimeMillis() - start));
    }
```

で，実行結果が下記の通り．

```
2000000 : 111
```

正確な値かつ，111msecということで結構早くなります．ということで，たかだか普通のintを足し算するときはちゃんとAtomicIntegerを使いましょう．

# おまけ
AtomicLongっていうのもありますが，AtomicFloatとかAtomicDoubleってのはありません．
これは自分で作れってことらしいです．まぁintとlongがあれば作れるけどさ・・・．

# 追記
[こっちの方法で](http://qiita.com/shinido/items/1b6778533291ede53f6f)もっとちゃんと計ってみました．結果はそんなに変わってませんが．