- PR -

SpringとStrutsの連携時のスレッドセーフの扱い

投稿者投稿内容
yos
会議室デビュー日: 2006/10/02
投稿数: 6
投稿日時: 2006-10-02 18:57
質問です。

以下の場合、スレッドセーフになりますか?

・SpringとStrutsの連携で「DelegatingActionProxy」を使用
・Service層のオブジェクトをSetterインジェクションしてインスタンス変数に保持
 ※Service層のオブジェクトはスレッドセーフを意識していない


StrutsはスレッドセーフにするにはActionでインスタンス変数を使用しないとなると
↑の場合、スレッドセーフにならないような気がします。


もし、スレッドセーフにならないとして、ActionSupportを使用する、synchronizedする
以外でいい方法があれば教えてください。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-10-02 22:54
背景がつかみにくいのですが、スレッドセーフにならないと思う理由は何でしょう?

ActionといっているのはHTTPリクエストの実処理を書くクラスのことですよね?
確かに、一般的にServletはリクエスト単位でスレッドが立ち上がるので
その都度インスタンスを生成するセッティングにでもしない限りは
インスタンス変数を用いるとスレッドセーフとならないことが多いですね。

ところで、インミュータブルなオブジェクト、つまり不変オブジェクトですが、
それに関して言えばsynchronizedやvolatileを設定しなくとも
スレッドセーフとなりえます。
ま、読み取るだけなら別に同期はいらないわけで。
書き込みがされる場合に注意が必要となるわけですよね。

さて、DIでActionクラスになんらかの設定をするのだと思いますが、
これはコンテナの起動時に行われるはずです。
ですから、「初期化が完了したかどうか」については心配する必要はない。
それ以後にDIでインジェクションされたオブジェクトが変更されるかどうか?
そのオブジェクトがフィールドを持たないステートレスなオブジェクトで
単にポリモーフィズムするためだけのオブジェクトであったら?

と、まぁそんなわけですよ。
yos
会議室デビュー日: 2006/10/02
投稿数: 6
投稿日時: 2006-10-03 00:45
ご回答ありがとうございます。

言葉足らずでしてすみませんでした。

nagiseさんのおっしゃるとおりActionクラスはHTTPリクエストの実処理を書くクラスのことです。
暗黙でStrutsを前提にしてしまっていたたためActionとしてしまいました。


スレッドセーフにならないと思う理由は以下の理由からです。

・ActionにDIされているService層オブジェクトのフィールドには
 Domain層オブジェクト(ここではDB操作を行うDAO)がDIされている。
・DB操作は更新処理が伴う
・DAOはiBatisのSqlMapClientDaoSupportクラスをextendsしている。
 ※設定は後述

つまり、ActionのフィールドにDIされたオブジェクトはステートレスなオブジェクトでなく、書き込み処理があるということです。

そのため、インスタンス変数の扱いには注意しなければならないだろうと思い
今回、ここで質問させていただきました。



※DAOのDI設定(参考:http://canetrash.seesaa.net/article/2068021.html)

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property>
<property name="url"><value>jdbc:hsqldb:.</value></property>
<property name="username"><value>postgres</value></property>
<property name="password"><value>postgres</value></property>
</bean>

<!-- Transaction manager for a single JDBC DataSource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref local="dataSource"/></property>
</bean>

<!-- SqlMap setup for iBATIS Database Layer -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation"><value>sql-map-config.xml</value></property>
<property name="dataSource"><ref local="dataSource"/></property>
</bean>

<!-- ========== DAO DEFINITIONS: IBATIS IMPLEMENTATIONS ======== -->
<bean id="userDao" class="ibatis.dao.sqlmap.UserSqlMapDao">
<property name="sqlMapClient"><ref local="sqlMapClient"/></property>
</bean>

nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-10-03 08:54
引用:

yosさんの書き込み (2006-10-03 00:45) より:
スレッドセーフにならないと思う理由は以下の理由からです。

・ActionにDIされているService層オブジェクトのフィールドには
 Domain層オブジェクト(ここではDB操作を行うDAO)がDIされている。
・DB操作は更新処理が伴う
・DAOはiBatisのSqlMapClientDaoSupportクラスをextendsしている。
 ※設定は後述



う〜む。DB操作は通常はJavaの同期とは別にトランザクション管理される
ものですから、そこでスレッドセーフにされているのが前提でしょう。
ですから、DBへの書き込み以外の処理でスレッドセーフではない
処理があるのだろうか?ということになります。

DB操作に当たって、DAOのクラス内で保持しているフィールドが書き換わる
という事象があるのだとすればそれはステートフルだと思いますが、
通常は複数スレッドから同時に参照/更新をかけるフィールドは
保持しないものではないでしょうか。
yos
会議室デビュー日: 2006/10/02
投稿数: 6
投稿日時: 2006-10-03 10:56
ご回答ありがとうございます。

>DB操作に当たって、DAOのクラス内で保持しているフィールドが書き換わる
>という事象があるのだとすればそれはステートフルだと思いますが、

DAOクラス内にフィールドは作成しておらず、それを書き換えるような処理もありません。


>DB操作は通常はJavaの同期とは別にトランザクション管理される
>ものですから、そこでスレッドセーフにされているのが前提でしょう。

説明が下手で回りくどい感じになってしまってすみません。
私は今回始めてStruts + Spring + iBatis を使用するのですが、ブラックボックスが多いため
↓のようなことを懸念してしまいました。

---------------------------------------------------------------------------------
1. DAOはiBatisのSqlMapClientDaoSupportクラスをextends
2. DAOをService層のフィールドに保持し、そのService層オブジェクトはPresentation層
 (Action)のフィールドに保持
 → Presentation層でスレッドであるため、スレッド間で1つのService層オブジェクト
  (=その中のDAO)を共有
 →(iBatisのソースをある程度追ったが、どのような形でjava.sql.Connectionオブジェクト
  を持っているか分からなかったため)Connectionオブジェクトも共有されてしまい、
  スレッド間でトランザクションの不整合が発生する??
---------------------------------------------------------------------------------

しかし、nagaseさんのおっしゃるとおり、Javaの同期とは別にトランザクション管理されるものであり、
そのトランザクション管理をコンテナ(今回はSpring)に任せてしまえば、↑の懸念のような
トランザクションの不整合は発生しないという理解でよろしいでしょうか?

※もちろんトランザクション属性、独立性レベルは意識する必要ありますが。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-10-03 12:37
引用:

yosさんの書き込み (2006-10-03 10:56) より:
しかし、nagaseさんのおっしゃるとおり、Javaの同期とは別にトランザクション管理されるものであり、
そのトランザクション管理をコンテナ(今回はSpring)に任せてしまえば、↑の懸念のような
トランザクションの不整合は発生しないという理解でよろしいでしょうか?

※もちろんトランザクション属性、独立性レベルは意識する必要ありますが。



トランザクションをきっちり管理しているにも関わらず、
Javaの同期がうまく取れないがためにスレッドセーフではないという
O/Rマッピングなんて使い物になりません

Connectionの取得の部分では最低限スレッドセーフにしてあるでしょうしね。
そこんとこが同期とられてなければ即刻バグ報告があがることでしょう。

個別製品の中身まではちょっと抑えていませんが、
DIで突っ込んだオブジェクトに複数スレッドで共有されるようなフィールドが
ない限りは問題ないと思います。(誤りがあれば突っ込み歓迎します)

このあたりDIでは「Servletではフィールドは使うな」みたいに
安直に注意勧告しにくいのが難点ですよね。
yos
会議室デビュー日: 2006/10/02
投稿数: 6
投稿日時: 2006-10-03 13:03
引用:
トランザクションをきっちり管理しているにも関わらず、
Javaの同期がうまく取れないがためにスレッドセーフではないという
O/Rマッピングなんて使い物になりません

Connectionの取得の部分では最低限スレッドセーフにしてあるでしょうしね。
そこんとこが同期とられてなければ即刻バグ報告があがることでしょう。



nagiseさんの説明に「確かに、なるほど」と思わせられました。

理論的にはスレッド間でフィールドの更新がなければ問題なさそうな気がします。
あとは性能試験で負荷をかけて実感をもてる状態にしようと思います。

色々とご回答ありがとうございました。
ykSiR
会議室デビュー日: 2006/10/03
投稿数: 16
投稿日時: 2006-10-03 14:08
はじめまして。

引用:

yosさんの書き込み (2006-10-03 00:45) より:

・DAOはiBatisのSqlMapClientDaoSupportクラスをextendsしている。



SqlMapClientDaoSupportとは、org.springframework.orm.ibatis.support.SqlMapClientDaoSupport
のことでしょうか?

そうであれば、ConnectionはThreadLocalに関連付られているので、スレッドセーフとなります。

スキルアップ/キャリアアップ(JOB@IT)