[Seasar-user:3709] 複数スレッドで同時にcomponentを取得するとロックする(長文)
Asarima
[E-MAIL ADDRESS DELETED]
2006年 5月 30日 (火) 13:39:27 JST
Asarimaです。
私はSeasarを使用したSwingアプリケーションを開発しております。
今までまったく問題なかったのですが、最近になって、アプリケーションの
ログイン画面からログインした時に、コンテナからコンポーネントを取得する
時にアプリケーションがロックする問題が起こっています。
大変長くなりますが、もしよろしければ、今から説明することについてより
よい対策があれば教えていただけると助かります。
1.前提とするコード
まず私は、Mainを以下のように書いています。
---
// S2コンテナを作成
S2Container container = (S2Container)S2ContainerFactory.create
(PATH);
// ログイン画面をロード
FrameUI loginFrame = (FrameUI)container.getComponent("loginFrame");
// ログイン画面を表示
loginFrame.open();
// 自動登録を有効にする
container.init();
---
ログイン画面のコンポーネントを取得し、それを表示した後にcontainer.init
を実行しています。こうする理由は、ログイン画面を早く表示したいからです。
「早く」というのは、AutoRegisterでかなりの数のコンポーネントを登録するの
で、container.init後にログイン画面を表示していては遅すぎるということです。
そのために、S2ContainerFactory.createで最低限必要なコンポーネントを登
録し、その後で必要なコンポーネントを自動登録させています。
FrameUIはJFrameのラップクラスのようなものです。
2.問題を起こすための操作
ログイン画面が表示されたら、認証情報(いわゆるユーザーIDとパスワード)
を入力してボタンをクリックします。
これが早すぎると、アプリケーションがロックすることがあります。
3.確認できたこと
クリックして認証処理を行った後、container.initで登録されるコンポーネン
トの一つを、S2Container.getComponentメソッドで取得しようとしているのです
が、これがコンポーネントを返さずロックします。
そこで、アプリケーションがロックされている時の各スレッドのスタックト
レースを見て、ある2つのスレッドがwaitしているのを見つけました。
1つ目のスレッドのスタックトレースは以下の通りです。
これはcontainer.initによるAutoRegisterの真っ最中かと思います。
---
スレッド [main] (中断中)
SingletonComponentDeployer.deploy() 行: 50
ComponentDefImpl.getComponent() 行: 94
S2ContainerImpl.getComponent(Object) 行: 109
BindingTypeShouldDef(AbstractBindingTypeDef).bindAuto(ComponentDef,
PropertyDesc, Object) 行: 75
BindingTypeShouldDef.doBind(ComponentDef, PropertyDesc, Object) 行:
39
BindingTypeShouldDef(AbstractBindingTypeDef).bind(ComponentDef,
PropertyDef, PropertyDesc, Object) 行: 64
AutoPropertyAssembler.assemble(Object) 行: 48
SingletonComponentDeployer.assemble() 行: 80
SingletonComponentDeployer.deploy() 行: 51
SingletonComponentDeployer.init() 行: 96
ComponentDefImpl.init() 行: 284
S2ContainerImpl.init() 行: 379
LoginMain.main(String[]) 行: 43
---
2つ目のスレッドのスタックトレースは以下の通りです。
ログイン画面のボタンクリックでコンポーネントを取得するところだと思いま
す。
---
スレッド [AWT-EventQueue-0] (中断中)
SingletonComponentDeployer.deploy() 行: 50
ComponentDefImpl.getComponent() 行: 94
S2ContainerImpl.getComponent(Object) 行: 109
BindingTypeShouldDef(AbstractBindingTypeDef).bindAuto(ComponentDef,
PropertyDesc, Object) 行: 75
BindingTypeShouldDef.doBind(ComponentDef, PropertyDesc, Object) 行:
39
BindingTypeShouldDef(AbstractBindingTypeDef).bind(ComponentDef,
PropertyDef, PropertyDesc, Object) 行: 64
AutoPropertyAssembler.assemble(Object) 行: 48
SingletonComponentDeployer.assemble() 行: 80
SingletonComponentDeployer.deploy() 行: 51
ComponentDefImpl.getComponent() 行: 94
S2ContainerImpl.getComponent(Object) 行: 109
BindingTypeShouldDef(AbstractBindingTypeDef).bindAuto(ComponentDef,
PropertyDesc, Object) 行: 75
BindingTypeShouldDef.doBind(ComponentDef, PropertyDesc, Object) 行:
39
BindingTypeShouldDef(AbstractBindingTypeDef).bind(ComponentDef,
PropertyDef, PropertyDesc, Object) 行: 64
AutoPropertyAssembler.assemble(Object) 行: 48
SingletonComponentDeployer.assemble() 行: 80
SingletonComponentDeployer.deploy() 行: 51
ComponentDefImpl.getComponent() 行: 94
S2ContainerImpl.getComponent(Object) 行: 109
TopMenuManagerImpl.getComponent(Object) 行: 64
TopMenuManagerImpl.getStartFrameUI() 行: 50
LoginFrameImpl.btnLoginAction(ActionEvent) 行: 165
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) 行:
使用不可 [ネイティブ・メソッド]
NativeMethodAccessorImpl.invoke(Object, Object[]) 行: 使用不可
DelegatingMethodAccessorImpl.invoke(Object, Object[]) 行: 使用不可
Method.invoke(Object, Object...) 行: 使用不可
MethodUtil.invoke(Method, Object, Object[]) 行: 42
ActionMethodInvoker.invokeMethod(AWTEvent) 行: 110
ActionMethodInvoker.actionPerformed(ActionEvent) 行: 120
JButton(AbstractButton).fireActionPerformed(ActionEvent) 行:
使用不可
AbstractButton$Handler.actionPerformed(ActionEvent) 行: 使用不可
DefaultButtonModel.fireActionPerformed(ActionEvent) 行: 使用不可
DefaultButtonModel.setPressed(boolean) 行: 使用不可
JButton(AbstractButton).doClick(int) 行: 使用不可
JButton(AbstractButton).doClick() 行: 使用不可
ButtonEnterActionAdaptor.keyPressed(KeyEvent) 行: 22
JButton(Component).processKeyEvent(KeyEvent) 行: 使用不可
JButton(JComponent).processKeyEvent(KeyEvent) 行: 使用不可
JButton(Component).processEvent(AWTEvent) 行: 使用不可
JButton(Container).processEvent(AWTEvent) 行: 使用不可
JButton(Component).dispatchEventImpl(AWTEvent) 行: 使用不可
JButton(Container).dispatchEventImpl(AWTEvent) 行: 使用不可
JButton(Component).dispatchEvent(AWTEvent) 行: 使用不可
DefaultKeyboardFocusManager(KeyboardFocusManager).redispatchEvent(
Component, AWTEvent) 行: 使用不可
DefaultKeyboardFocusManager.dispatchKeyEvent(KeyEvent) 行: 使用不可
DefaultKeyboardFocusManager.preDispatchKeyEvent(KeyEvent) 行:
使用不可
DefaultKeyboardFocusManager.typeAheadAssertions(Component, AWTEvent)
行: 使用不可
DefaultKeyboardFocusManager.dispatchEvent(AWTEvent) 行: 使用不可
LoginFrame$$EnhancedByS2AOP$$40ece0(Component).dispatchEventImpl(
AWTEvent) 行: 使用不可
LoginFrame$$EnhancedByS2AOP$$40ece0(Container).dispatchEventImpl(
AWTEvent) 行: 使用不可
LoginFrame$$EnhancedByS2AOP$$40ece0(Window).dispatchEventImpl(
AWTEvent) 行: 使用不可
LoginFrame$$EnhancedByS2AOP$$40ece0(Component).dispatchEvent(
AWTEvent) 行: 使用不可
EventQueue.dispatchEvent(AWTEvent) 行: 使用不可
EventDispatchThread.pumpOneEventForHierarchy(int, Component) 行:
使用不可
EventDispatchThread.pumpEventsForHierarchy(int, Conditional,
Component) 行: 使用不可
EventDispatchThread.pumpEvents(int, Conditional) 行: 使用不可
EventDispatchThread.pumpEvents(Conditional) 行: 使用不可
EventDispatchThread.run() 行: 使用不可
---
SingletonComponentDeployer.deployメソッドがwaitしているのでそれを抜き
出し、そこで参照しているcomponentのStringを書いたのが以下です。
---
スレッド [main] (中断中)
SingletonComponentDeployer.deploy() 行: 50
component= JinjiMenuFrameImpl$$EnhancedByS2AOP$$1a40fff (ID=4164)
SingletonComponentDeployer.deploy() 行: 51
component= JinjiDataOutputFrameImpl$$EnhancedByS2AOP$$1ddc55f (ID=4165)
スレッド [AWT-EventQueue-0] (中断中)
SingletonComponentDeployer.deploy() 行: 50
component= JinjiDataOutputFrameImpl$$EnhancedByS2AOP$$1ddc55f (ID=4165)
SingletonComponentDeployer.deploy() 行: 51
component= JinjiSearchFrameImpl$$EnhancedByS2AOP$$1b1d931 (ID=4166)
SingletonComponentDeployer.deploy() 行: 51
component= JinjiMenuFrameImpl$$EnhancedByS2AOP$$1a40fff (ID=4164)
---
4.推測したこと
これを見て、直感的に、スレッドのデッドロックが起こっているのでは、と
思いました。deployメソッドにはsynchronized修飾子がついています。
私は、以下のようなことが起こっていると推測しました。
コンポーネントAがBに依存し、CがDに依存しているとする。
2つのスレッドでそれぞれ同時にコンポーネントA、Cを取得すると、
AのdeployでBのdeployが発生、CのdeployでDのdeployが発生するため、
CのdeployがAのdeployを待ち、BのdeployがCのdeployを待つので、
デッドロックとなる。
5.推測をもとにして考えた対策
とにかく複数のスレッドで同時にdeployされないようにします。
(a)container.initを後で実行するのをあきらめる
(b)ログイン画面は表示するが、container.initが終わるまでは
ログイン後のコンポーネントの取得をwaitする
以上です。
Seasar-user メーリングリストの案内