staticとスレッドセーフの罠

皆さんご存知 static

特に何も考えずにプログラムする場合は便利ですよね、インスタンス化しなくてもどこからでも呼び出せるし。そもそもjavaの入門は「static void main(String[] args) {~}」なんだからここで使用するメソッドも当然staticになるわけで…

これが諸悪の根源のような気さえする。

AzureでもAWSでもそうなんだけど、元々マルチスレッド前提で起動させるアプリケーションで安易にstaticで変数やメソッド作ると知らないところで痛い目にあいます「沈黙の臓器」肝臓みたいなもんですね。(バグったりバグらなかったりいつの間にかメモリリークしたりするので見つけるのも大変、単体テストや結合テストでは絶対にバグの発見ができず、負荷テストでシステムの動きをちゃんと確認していたら、もしかしたら発見できるかも…大概はリリース後に発覚して大目玉を食らう)

★まず極力staticは使わないようにしましょう。これが大前提。使う場合は慎重に慎重を重ねて、よく吟味したうえで使いましょう。以下、やっちゃいけないパターンです。

1.AWSLambda(又はコンテナを使用したアプリケーションサービス等)でstaticなメンバー変数を評価して値の更新をする。

  • アプリの実行一回分が終わったらプロセスも死ぬ。ように見えるかもしれませんが、裏ではコンテナが起動していて、こちらの思った時にプロセスが死んでいるわけではありません。
    • その証拠に、ホットスタートというやり方が存在し、ヘルスチェック(一定間隔で処理を流させるようなトリガーを引く)でプロセスを殺さずに永続させ、初期起動時間の短縮を図ることができます。実行ソースがJavaだとjarファイルの解凍やらJVMの起動やらで初期起動に鬼のような時間がかかるので、これを用いることがあるそうな。
    • 補足:起動時間が最も速いのがGO、2番目がPython、最も遅いのがJava(ただしホットスタートであれば、処理時間で最も速いのがGO、2番目がJava)
  • そもそもstaticの値を変更するというのが恐ろしい。

2.List (Java) や Dictionary (c#) をstaticで宣言して、プロセス内で単一の値を持たせておく。

  • 別に考えとしては悪くない、コネクションを張ったりするときによく使うかもしれない。ただし、JavaのListはスレッドセーフではないので、ListなのにOutOfIndexが発生したりします(hashmapを使用するときも同様)。うまいやり方は、Listにアクセスするときにsynchronizedしたりするんですが、詳しくは以下のサイト「https://qiita.com/hitomatagi/items/c43267d4fd0342147ab7」に丁寧に記載されていたので参考にしてください。
  • c#のDictionaryは、データを突っ込む用のメソッドがスレッドセーフではなかったりするらしいので、今はConcurrentDictionaryというスレッドセーフなクラスが用意されているようなので、Framework 4.7.2 以降を使用しているならこれを使うのがいいんじゃないでしょうか?

 

他にもやらかすパターンはたくさんあると思いますので、くれぐれも注意して、staticの罠にはまらないようにしてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA