• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:スレッドセーフにするべきクラスについて。)

スレッドセーフにするべきクラスについて

このQ&Aのポイント
  • マルチスレッドで動くクラスはスレッドセーフにしなければならないが、どのクラスがマルチスレッドで動くのか判定する基準が分からない
  • サーブレットやJSP、Springでシングルトンにしたクラスはスレッドセーフを意識する必要がある
  • クラスを作成する際にメンバ変数に状態を保持する変数を入れてもよいか悩んでいる

質問者が選んだベストアンサー

  • ベストアンサー
  • askaaska
  • ベストアンサー率35% (1455/4149)
回答No.4

Servletにメンバ変数を持たせる という発想自体やらない方がいいわ。 別に悪ではないけど。 極論を言うを、 ValueObject以外、すべてのクラスはメンバ変数を持たない でスレッドセーフを保てるわ。 それといまさら気づいたんだけど あなたは「スレッドセーフなクラス」に焦点を当てているようね。 スレッドセーフはクラスではなく、 全体の構造で考えるべきだわ。 つまり、すべての層でスレッドセーフが保てていて、 初めてそのスレッド(アクセスとかアクションとかServletリクエストとか表現はなんでも)は スレッドセーフであるといえるわ。 >(例えばexecuteの戻り値にEntityを返却するように変更するなど) そうね。 でもそもそもServletのメンバ変数にするほうが問題なんだけど。 >public DataBaseAccessor extends Thread { DataBaseAccessor自信は今のままでいいわよ。 DataBaseAccessorそのものよりも DataBaseAccessorの使い方に問題があるのよ。 DataBaseAccessorに罪はないわ。 あ、でも、DataBaseAccessor extends Threadのインスタンスを あちこちのスレッド(アクセスとかアクションとか以下略)で使いまわすっていうなら話は別だけど。 そんな実装はしないでほしいわ。 という風になると、やっぱり問題にしているのは DataBaseAccessorが正しく使われているかどうかをどう判定するのか てことになるのかしら。 正解を言うと、呼び出し元を判断できる場所まで追いかける、てことになるわね。 DataBaseAccessorが使われるたびに スレッドIDとDataBaseAccessorのインスタンスを管理して 同じDataBaseAccessorのインスタンスが別のスレッド内で利用されていないかどうかを確認する、 なんて手もあるかも?とか考えたりするけど 実際正常に判断できるのかわからないわ。

_alias_
質問者

お礼

お返事送れてごめんなさい。 >あなたは「スレッドセーフなクラス」に焦点を当てているようね。 >スレッドセーフはクラスではなく、 >全体の構造で考えるべきだわ。 仰るとおりだと思います。 >正解を言うと、呼び出し元を判断できる場所まで追いかける、てことになるわね。 やはりそういう話になってしまいますか。 色々教えて頂いて有難うございました。まだ正直、完全に理解できているわけではないのですが、だいぶすっきりしました。またマルチスレッドについて質問をする場合もあるかと思いますが、もしよろしければまたアドバイス頂ければと思います。 ありがとうございました。

その他の回答 (3)

  • root139
  • ベストアンサー率60% (488/809)
回答No.3

身もふたも無い書き方ですが、漠然と「スレッドセーフにしなければいけないコード」となると下記の様な感じですね。 ・複数のスレッドからアクセスされる可能性の有るもの。 ・フレームワーク・コーディング規約などでスレッドセーフにするように指示されているもの たとえば、 Struts1.x の action オブジェクトは複数のスレッドからアクセスされるのでスレッドセーフにする必要が有るとされています。 で、1の補足説明から見て、質問者さんの疑問は、 「スレッドセーフにしなければいけないコードから参照するコードは、どこまでスレッドセーフにしなければいけないか?」 ということでよいでしょうか? 端的にいうと「スレッド拘束されていないものは、スレッドセーフにすべき」となるかと。 例えば、ローカル変数もしくはThreadLocalのみに保持され、外部に公開されないオブジェクトなどがスレッド拘束されているものにあたります。 詳しいことは下記の本を参照して下さい。 Java並行処理プログラミング http://www.amazon.co.jp/dp/4797337206 ちなみに、変化する状態を持たないオブジェクトは本質的にスレッドセーフなので特に意識する必要は無いです。 (ただし、親クラスの状態は見落としがちなので注意) 「状態」は「フィールド」と読み替えても良いでしょう。

参考URL:
http://www.ibm.com/developerworks/jp/java/library/j-jtp09263/index.html
_alias_
質問者

お礼

お返事有難うございます。 >「スレッドセーフにしなければいけないコードから参照するコードは、どこまでスレッドセーフにしなければいけないか?」 はい。1点はまさに上記が質問内容になります。 こう書くと分かりやすいです。 もう1点は「複数のスレッドからアクセスされる可能性の有るもの。」と書かれているようなことをどのように判別すればよいかになります。 質問とは少し外れますが、actionクラスもスレッドセーフではないのですね。認識を間違えていました・・・。

  • askaaska
  • ベストアンサー率35% (1455/4149)
回答No.2

んー よくわからないわ(汗 > 複数のスレッドから呼ばれるAというクラス これはシングルトン? それとスレッドごとにインスタンスが生成されるの? > Aのメンバ変数Bは複数のスレッドで共有されると考えています ServletContext的なものを連想すればいいのかしら。 > Bにオブジェクトの状態を保持させてはいけない このオブジェクトが何を指しているのかしら。 スレッドの状態の間違い? ちなみに[オブジェクトID,状態]のようなMAPなら持ってもかまわないわよね。 > またAのメソッドの中でローカル変数Cを生成したとします。~ メソッド内のローカル変数のライフサイクルがそのメソッド内のみなら 複数スレッドから共有されない、これは問題ないわ。 でも、引数から渡された値を格納したりすると面倒かもね。 プリミティブ型だったりStringだったり、cloneしていたりすれば別だけど。 >Dはスレッドごとに独立している 取り扱いさえ間違えなければ CもDも、スレッドごとにインスタンスが作られるわ。 でも、前述したように引数で渡されてきた値を格納するのだと 保証はないわね。 もし、変数Cに引数で渡されてきた値を格納するのであるならば、 その引数のオブジェクトもスレッドごとに独立しているなら問題ないわ。 なんか求められている回答と違う気がしなくもないんだけど まあ、やり取りしているうちに近づいていくことを期待ね。

_alias_
質問者

お礼

お返事有難うございます。 ごめんなさい。説明が下手でいつも分かりづらくなってしまいます。 >これはシングルトン? >それとスレッドごとにインスタンスが生成されるの? シングルトンになります。スレッドごとに同じインスタンスを参照します。 >ServletContext的なものを連想すればいいのかしら。 例えばDBの検索結果などを考えています。 >このオブジェクトが何を指しているのかしら。 >スレッドの状態の間違い? >ちなみに[オブジェクトID,状態]のようなMAPなら持ってもかまわないわよね。 例えばスレッドaがDBの検索結果を保持しているのに、スレッドbが結果を書き換えてはいけない、ということを指しています。 >メソッド内のローカル変数のライフサイクルがそのメソッド内のみなら >複数スレッドから共有されない、これは問題ないわ。 >取り扱いさえ間違えなければ >CもDも、スレッドごとにインスタンスが作られるわ。 有難うございます。この認識は合っているようで安心しました。 もう少し具体的に書いてみます(余計分かりづらくならないか不安ですが)。 開発にあたりDBにアクセスして結果を保持するクラスを作ることになりました(クラス設計としてどうなのかは一先ず置いておきます)。 public DataBaseAccessor { // DaoはSQLを発行するクラスと考えてください。 private Dao dao; // Entityはテーブルのカラム要素のクラスと考えてください。 private Entity data; public void execute(Entity criteria) { ・ ・ ・ data = dao.execute(criteria); ・ ・ ・ } } Daoクラスについては状態が変更されるようなクラスではない(と仮定する)のですが、EntityはDBの検索結果を保持するので、勝手に書き換えられると非常に困ります。 例えばこのDataBaseAccesorクラスがServletクラスのメンバ変数として生成された場合、複数のスレッドで共有されるので勝手に書き換えられると認識しています。 ですので、もし仮にServletクラスのメンバ変数として使われるのであれば、DataBaseAccesorをスレッドセーフにしなければなりません(例えばexecuteの戻り値にEntityを返却するように変更するなど)。 また例えば「public DataBaseAccessor {」ではなく、「public DataBaseAccessor extends Thread {」の場合も同様にスレッドセーフを意識しないといけないと認識しています。 また例えばstaticなメンバ変数として使われる場合も同様と認識しています。 ここで当初の質問の内容というのが、DataBaseAccesorを作成したのはいいが、このクラス自体はスレッドセーフじゃない。今回カスタムタグのメンバ変数として使用されることになるが、カスタムタグのクラスってServletクラスみたいに複数スレッドから呼ばれるの???どこを見れば「呼ばれる」or「呼ばれない」が判定できるの? というのが質問の趣旨になります。 ・Servletのメンバ変数は複数スレッドで共有される。 ・staticなメンバ変数は共有される。 ・springのDI登録時にシングルトンタイプを選択した場合、メンバ変数は複数スレッドで共有される。 というのは今までの知識で知っているので、こういう場合はスレッドセーフを意識しようと思うのですが、他にどのような場合に自分が作成するクラスをスレッドセーフにしなければならないかが知りたいのです。

  • askaaska
  • ベストアンサー率35% (1455/4149)
回答No.1

あるインスタンスが複数の人(というかスレッド)が共有する このとき、そのコードはスレッドセーフではない、ということよ。 たとえばJavaでアクセスカウンター(累計)を作る。 そしてカウントアップした数字はVM上に保存するとするわね。 この数字は全ユーザがインクリメントする必要があるわ。 そのとき、この数字がスレッドセーフであったら のべアクセス数を出すことができなくなっちゃうわね。 逆を言うと、スレッドセーフでない変数があった場合 その変数はAさんが処理している間にBさんにより書き換えられてしまう恐れがあるわ。 じゃあ、そういう変数を、インスタンスをどう探せばいいのか という問題になるわね。 そのときのキーワードになるのは static よ。 staticな変数がどんな意味を持つのか どんな役割になるのかを考えれば 理由は見えてくると思うわ。

_alias_
質問者

お礼

お返事ありがとうございます。 staticメンバ変数を忘れていました。確かにそうです。 ただ今回の質問はあるクラスを作ったけど、このクラスって複数のスレッドから同時に参照されるような使われ方をするのだろうか?という疑問でした。自分の作ったクラスがServletだったり、Threadを拡張してたり、staticメンバ変数を保持している場合は、スレッドセーフにしなければと思えます。 ただ例えばServletクラスはスレッドセーフにしないといけないけど、Servletクラスのメソッドで生成したクラスもスレッドセーフになっていないといけないのかとか、作成したクラスをシングルトンモードでSpringにDI登録した際に、そのクラスのメソッド内で生成する別のクラスにメンバ変数がある場合、そのメンバ変数は複数スレッドから同時にアクセスされるのかどうかとか・・・。 うまく説明できていませんが、複数のスレッドから呼ばれるAというクラスがあります。Aのメンバ変数Bは複数のスレッドで共有されると考えていますので、Bにオブジェクトの状態を保持させてはいけないと考えています。 ※ここまでで間違っていたらご指摘頂けると助かります。 またAのメソッドの中でローカル変数Cを生成したとします。Aのメソッドの中のCは複数からのスレッドから共有されないと考えています。またCにメンバ変数Dがあるとします(static除く)。 Aのメソッドが複数のスレッドから呼び出させてもCはスレッドごとに生成され、Dはスレッドごとに独立していると考えてもよいでしょうか? ごちゃごちゃな記述で見難いと思います。すみません。