質問: 今まで避けてきたワードなのですが、 そうも行かなくなりそうなので質問させてください。 シングルスレッド、マルチスレッド、スレッドローカル、シングルトン、プロトタイプなどのキーワードで検索してみたのですがいまいち理解できません。 以下の認識で合っているか教えて下さい。 前提はspringframeworkを使用したWebアプリです。 ・シングルトン システムで1インスタンスを使い回す。 つまり、アプリが起動して最初にインスタンス生成されたものをずっと使い回す。当たり前だがリクエストごとの状態を持たせてはいけない。 ・シングルスレッド 一つのスレッドで全ての処理が行われる実行環境。 一つのインスタンスに同時にアクセスされることがないため安全だが効率が悪い。 (リクエストの概念がないバッチアプリは一回実行されるだけなのでシングルスレッド?) ・マルチスレッド 複数のリクエストを同時に処理する実行環境。 現在のJavaプログラムはこれ? スレッドローカルでないクラス変数は全て同時にアクセスされる可能性があるので、リクエストごとの状態を持つインスタンスはクラス変数にしてはいけない(必ずメソッド内で定義&インスタンス生成)。 ・スレッドローカル threadLocalを使用して作成したクラス変数のこと。 シングルトンならシステムで1インスタンスだが、スレッドローカルはスレッド(≒リクエスト?)ごとに1インスタンスだから、例えば、 多くのメソッドで必要になる変数だがメソッドの引数に毎回同じ指定なんてしたくない場合に便利。 ・スレッドセーフ マルチスレッド環境でも同時に処理されることがないクラス変数。以下のようにしてsynchronizedで囲むだけ。排他がかかるのでパフォーマンスは落ちる。 synchronized(変数){ //指定した変数に対して行う処理。 //この中に書いた処理は全て終了するまで排他制御がかかる。 } これらについて認識合っているか教えて欲しいです。 よろしくお願いします。
回答: こんにちは。 まず、「スレッド」の概念は理解されていますでしょうか? プログラムを実行する「主体」みたいなものですね。それをスレッドと呼びます。プロセスが起動されたら、そのエントリーポイント(CやC++はmain()関数です。Javaはmain()メソッド?)からスレッドが実行を開始します。 この時点ではシングル・スレッドです。そのまま、サブスレッドを起動しなかった場合はシングル・スレッドのまま動作を継続します。最後にmain()からリターンするなどによりプログラムを終了します。 サブスレッドを起動するとマルチ・スレッド・プログラムとなります。同じプロセス内で起動されたスレッドは、同じプロセス内にあるメモリにアクセスできます。 今、ある変数xがあり、2つのスレッドaとbが変数xをインクリメントすると仮定します。 インクリメントする場合、そのメモリの内容を読み出して1加えて元のメモリへ書き込みます。これをRead-Modify-Writeといいます。 非常に運が悪く、2つのスレッドが変数xを同時にインクリメントしたとします。x==0の時に2つのスレッドがインクリメントした場合でも、期待する結果はx==2となることですね。 さて、同時と言っても同じメモリを全く同時にアクセスすることはできないよう、コンピュータのハードウェアが調整します。その結果、下記の順序でアクセスしたと仮定します。 1. スレッドaがread 2. スレッドbがread 3. スレッドaが読み出した値をインクリメント(1になる) 4. スレッドbが読み出した値をインクリメント(1になる) 5. スレッドaがwrite(xが1になる) 6. スレッドbがwrite(xは1のまま) ということで、残念ながら期待通りの動作になりません。 これがマルチ・スレッドの危険性です。もちろんインクリメントに限りません。他にも様々なパターンのリスクが存在しています。 このリスクを回避する仕組みを備えている機能は「スレッドセーフ」と呼ばれます。 また、スタック上のメモリとスレッド・ローカル・ストレージ上のメモリはそれを所有しているスレッドだけがアクセスできますので、安心してアクセスできます。逆にそれ以外のメモリは全てリスクを抱えています。 複数のスレッドが同時にアクセスすることがないよう、複数のスレッドで共有しないようにしたり、共有する場合はミューテックスで排他制御したりします。 因みに、シングルトンやスレッド・ローカル・ストレージは一種のグローバル変数ですから、使わないで済む場合はなるべく使わない方が良いです。(必要な時は当然使うべきですが)これはシングル・スレッド・プログラムの場合となんら変わりません。