サーブレットスレッドのセキュリティ問題を深く検討する
5988 ワード
サーブレットスレッドのセキュリティ問題を深く検討する
サーブレット/JSP技術はASP,PHPなどと比較してマルチスレッド運転により高い実行効率を有する.サーブレット/JSPのデフォルトはマルチスレッドモードで実行されるため、コードを記述する際にマルチスレッドのセキュリティの問題を非常に細かく考慮する必要があります.しかし、多くの人がサーブレット/JSPプログラムを作成する際にマルチスレッドセキュリティの問題に気づいていないため、作成したプログラムは少量のユーザーアクセス時に何の問題もなく、同時ユーザーが一定の値に上昇すると、奇妙な問題がしばしば発生する.
サーブレットのマルチスレッドメカニズム
サーブレットアーキテクチャはJavaマルチスレッドメカニズムに基づいて構築され、そのライフサイクルはWebコンテナが担当します.クライアントがサーブレットを最初に要求すると、サーブレットコンテナはwebに従う.xmlプロファイルは、このサーブレットクラスをインスタンス化します.新しいクライアントがサーブレットを要求すると、通常、サーブレットクラスはインスタンス化されません.つまり、複数のスレッドがこのインスタンスを使用しています.サーブレットコンテナは、図1に示すように、スレッドプールなどの技術を自動的に使用してシステムの動作をサポートします.
図1サーブレットスレッドプール
これにより、2つ以上のスレッドが同じサーブレットに同時にアクセスすると、複数のスレッドが同じリソースに同時にアクセスする場合があり、データが一致しなくなる可能性があります.したがって、サーブレットで構築されたWebアプリケーションでは、スレッドセキュリティの問題に注意しないと、書かれたサーブレットプログラムに発見しにくいエラーが発生します.
サーブレットのスレッドセキュリティの問題
サーブレットのスレッドセキュリティの問題は主にインスタンス変数の不適切な使用によるものであり,ここでは現実的な例で説明する.
このサーブレットには、サービスメソッドでユーザーの出力に値を割り当てるインスタンス変数outputが定義されています.1人のユーザがサーブレットにアクセスすると、プログラムは正常に動作しますが、複数のユーザが同時にアクセスすると、他のユーザの情報が他のユーザのブラウザに表示されるという問題が発生する可能性があります.これは深刻な問題だ.同時性の問題を際立たせ,テスト,観察を容易にするために,ユーザ情報をエコーする際に遅延した操作を実行した.Webに既に存在すると仮定します.xmlプロファイルに登録されているサーブレットには、既存の2人のユーザaとbが同時にアクセスする(2つのIEブラウザを起動したり、2台のマシンで同時にアクセスしたりすることができる)、すなわち同時にブラウザに入力する:
a: http://localhost: 8080/servlet/ConcurrentTest? Username=a
b: http://localhost: 8080/servlet/ConcurrentTest? Username=b
ユーザbがユーザaの復帰時間より少し遅い場合、図2に示すような出力が得られる.
図2 aユーザとbユーザのブラウザ出力
図2に示すように、Webサーバは、ユーザaとユーザbからの要求をそれぞれ処理する2つのスレッドを起動するが、ユーザaのブラウザには、ユーザaの情報がユーザbのブラウザに表示される空白の画面が得られる.このサーブレットにはスレッドが安全ではないという問題があります.次に、インスタンスを解析するメモリモデルから着手し、異なる時刻のインスタンス変数outputの値を観察して、サーブレットスレッドが安全でない原因を解析します.
JavaのメモリモデルJMM(Java Memory Model)JMMは主にスレッドとメモリの関係を規定するためである.JMMの設計によると、システムにはメインメモリ(Main Memory)が存在し、Javaのすべてのインスタンス変数はメインメモリに格納され、すべてのスレッドに対して共有されている.各スレッドには独自のワークメモリ(Working Memory)があり、ワークメモリはキャッシュとスタックの2つの部分から構成されており、キャッシュに保存されているのはプライマリメモリの変数のコピーであり、キャッシュはプライマリメモリと同期していない可能性があります.つまり、キャッシュ中の変数の変更はすぐにプライマリメモリに書き込まれていない可能性があります.スタックにはスレッドのローカル変数が保存されており、スレッド間ではスタック内の変数に直接アクセスできません.JMMによれば,論文で論じたサーブレットインスタンスのメモリモデルを図3に示すモデルとして抽象化できる.
図3サーブレット例のJMMモデル
次に、図3に示すメモリモデルに基づいて、図4に示すように、ユーザaとbのスレッド(略称aスレッド、bスレッド)が同時に実行された場合のサーブレット例に係る変数の変化およびスレッドの実行状況を解析する.
スケジューリング時刻
aスレッド
bスレッド
T1
サーブレットページへのアクセス
T2
サーブレットページへのアクセス
T3
output=aの出力username=aは5000ミリ秒休止し、CPUを譲る
T4
output=bの出力(プライマリメモリへの書き込み)username=bは5000ミリ秒休止し、CPUを譲る
T5
ユーザbのブラウザにaスレッドのusernameの値が出力され、aスレッドは終了する.
T6
ユーザbのブラウザにbスレッドのusernameの値が出力され、bスレッドが終了する.
図4サーブレット例のスレッドスケジューリング状況
図4から明らかなように、bスレッドによるインスタンス変数outputの変更は、aスレッドによるインスタンス変数outputの変更を上書きするため、ユーザaの情報がユーザbのブラウザに表示される.aスレッドが出力文を実行する場合、bスレッドによるoutputの変更がホストメモリにリフレッシュされていない場合、図2に示す出力結果は現れないため、これは偶然の現象にすぎないが、プログラムの潜在的な危険性をさらに増大させる.
スレッドの安全なサーブレットの設計
以上の解析から,インスタンス変数の不正な使用がサーブレットスレッドの不安全をもたらす主な原因であることが分かった.以下では,この問題に対して3つの解決策を示し,スキームの選択にいくつかの参考となる提案を示した.
1、SingleThreadModelインタフェースを実現する
このインタフェースは、システムが同じサーブレットに対する呼び出しをどのように処理するかを指定します.1つのサーブレットがこのインタフェースで指定されている場合、このサーブレットのサービスメソッドでは2つのスレッドが同時に実行されることはありません.もちろん、スレッドセキュリティの問題もありません.このメソッドは、前のConcurrent Testクラスのクラスヘッダ定義を次のように変更します.
2、共有データに対する操作を同期する
synchronizedキーワードを使用すると、保護されたセグメントに一度に1つのスレッドしかアクセスできないことが保証され、本論文のサーブレットはブロック操作を同期することでスレッドの安全を保証することができる.同期後のコードは次のとおりです.
3、インスタンス変数の使用を避ける
このインスタンスのスレッドセキュリティの問題は、インスタンス変数によって発生します.サーブレット内のメソッドでインスタンス変数が使用されない限り、そのサーブレットはスレッドセキュリティです.
上記のサーブレットコードを修正し、インスタンス変数をローカル変数に変更して同様の機能を実現します.コードは以下の通りです.
上記の3つの方法をテストし,いずれもスレッドセキュリティのサーブレットプログラムを設計できることを示した.しかし、サーブレットがSingleThreadModelインタフェースを実装すると、サーブレットエンジンは新しいリクエストごとに個別のサーブレットインスタンスを作成し、多くのシステムオーバーヘッドを引き起こすことになります.SingleThreadModelはサーブレット2にあります.4ではもう使用を提唱していない.同様に、プログラムで同期を使用して使用する共有データを保護すると、システムのパフォーマンスが大幅に低下します.これは、同期されたコードブロックが同じ時点で1つのスレッドしか実行できないため、顧客要求を同時に処理するスループットが低下し、多くの顧客がブロックされているためである.また、プライマリ・ストレージ・コンテンツとスレッドのワークメモリ内のデータの一貫性を確保するために、キャッシュを頻繁にリフレッシュすることは、システムのパフォーマンスにも大きく影響します.従って、実際の開発においても、サーブレット内の同期コードを回避または最小化しなければならない.Serletでインスタンス変数を使用しないことは、サーブレットスレッドのセキュリティを保証するための最良の選択です.Javaメモリモデルからも分かるように、メソッドの一時変数はスタックに空間を割り当て、各スレッドには独自のプライベートスタック空間があるため、スレッドのセキュリティに影響しません.
小結
サーブレットのスレッドセキュリティの問題は、大量の同時アクセス時にのみ発生し、発見しにくいため、サーブレットプログラムを作成する際に特に注意してください.スレッドセキュリティの問題は主にインスタンス変数に起因するため、サーブレットではインスタンス変数の使用を避ける必要があります.アプリケーション設計でインスタンス変数の使用を回避できない場合は、使用するインスタンス変数を保護するために同期を使用しますが、システムの最適なパフォーマンスを保証するために、可用性が最も低いコードパスを同期する必要があります.
原文出典:http://www.feeten.cn/html/jsp/01/20090102181631103402.html
作者:佚名
サーブレット/JSP技術はASP,PHPなどと比較してマルチスレッド運転により高い実行効率を有する.サーブレット/JSPのデフォルトはマルチスレッドモードで実行されるため、コードを記述する際にマルチスレッドのセキュリティの問題を非常に細かく考慮する必要があります.しかし、多くの人がサーブレット/JSPプログラムを作成する際にマルチスレッドセキュリティの問題に気づいていないため、作成したプログラムは少量のユーザーアクセス時に何の問題もなく、同時ユーザーが一定の値に上昇すると、奇妙な問題がしばしば発生する.
サーブレットのマルチスレッドメカニズム
サーブレットアーキテクチャはJavaマルチスレッドメカニズムに基づいて構築され、そのライフサイクルはWebコンテナが担当します.クライアントがサーブレットを最初に要求すると、サーブレットコンテナはwebに従う.xmlプロファイルは、このサーブレットクラスをインスタンス化します.新しいクライアントがサーブレットを要求すると、通常、サーブレットクラスはインスタンス化されません.つまり、複数のスレッドがこのインスタンスを使用しています.サーブレットコンテナは、図1に示すように、スレッドプールなどの技術を自動的に使用してシステムの動作をサポートします.
図1サーブレットスレッドプール
これにより、2つ以上のスレッドが同じサーブレットに同時にアクセスすると、複数のスレッドが同じリソースに同時にアクセスする場合があり、データが一致しなくなる可能性があります.したがって、サーブレットで構築されたWebアプリケーションでは、スレッドセキュリティの問題に注意しないと、書かれたサーブレットプログラムに発見しにくいエラーが発生します.
サーブレットのスレッドセキュリティの問題
サーブレットのスレッドセキュリティの問題は主にインスタンス変数の不適切な使用によるものであり,ここでは現実的な例で説明する.
Import javax.servlet. *;
Import javax.servlet.http. *;
Import java.io. *;
Public class Concurrent Test extends HttpServlet {PrintWriter output;
Public void service (HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {String username;
Response.setContentType ("text/html; charset=gb2312");
Username = request.getParameter ("username");
Output = response.getWriter ();
Try {Thread. sleep (5000); // ,
} Catch (Interrupted Exception e){}
output.println(" :"+Username+"<BR>");
}
}
このサーブレットには、サービスメソッドでユーザーの出力に値を割り当てるインスタンス変数outputが定義されています.1人のユーザがサーブレットにアクセスすると、プログラムは正常に動作しますが、複数のユーザが同時にアクセスすると、他のユーザの情報が他のユーザのブラウザに表示されるという問題が発生する可能性があります.これは深刻な問題だ.同時性の問題を際立たせ,テスト,観察を容易にするために,ユーザ情報をエコーする際に遅延した操作を実行した.Webに既に存在すると仮定します.xmlプロファイルに登録されているサーブレットには、既存の2人のユーザaとbが同時にアクセスする(2つのIEブラウザを起動したり、2台のマシンで同時にアクセスしたりすることができる)、すなわち同時にブラウザに入力する:
a: http://localhost: 8080/servlet/ConcurrentTest? Username=a
b: http://localhost: 8080/servlet/ConcurrentTest? Username=b
ユーザbがユーザaの復帰時間より少し遅い場合、図2に示すような出力が得られる.
図2 aユーザとbユーザのブラウザ出力
図2に示すように、Webサーバは、ユーザaとユーザbからの要求をそれぞれ処理する2つのスレッドを起動するが、ユーザaのブラウザには、ユーザaの情報がユーザbのブラウザに表示される空白の画面が得られる.このサーブレットにはスレッドが安全ではないという問題があります.次に、インスタンスを解析するメモリモデルから着手し、異なる時刻のインスタンス変数outputの値を観察して、サーブレットスレッドが安全でない原因を解析します.
JavaのメモリモデルJMM(Java Memory Model)JMMは主にスレッドとメモリの関係を規定するためである.JMMの設計によると、システムにはメインメモリ(Main Memory)が存在し、Javaのすべてのインスタンス変数はメインメモリに格納され、すべてのスレッドに対して共有されている.各スレッドには独自のワークメモリ(Working Memory)があり、ワークメモリはキャッシュとスタックの2つの部分から構成されており、キャッシュに保存されているのはプライマリメモリの変数のコピーであり、キャッシュはプライマリメモリと同期していない可能性があります.つまり、キャッシュ中の変数の変更はすぐにプライマリメモリに書き込まれていない可能性があります.スタックにはスレッドのローカル変数が保存されており、スレッド間ではスタック内の変数に直接アクセスできません.JMMによれば,論文で論じたサーブレットインスタンスのメモリモデルを図3に示すモデルとして抽象化できる.
図3サーブレット例のJMMモデル
次に、図3に示すメモリモデルに基づいて、図4に示すように、ユーザaとbのスレッド(略称aスレッド、bスレッド)が同時に実行された場合のサーブレット例に係る変数の変化およびスレッドの実行状況を解析する.
スケジューリング時刻
aスレッド
bスレッド
T1
サーブレットページへのアクセス
T2
サーブレットページへのアクセス
T3
output=aの出力username=aは5000ミリ秒休止し、CPUを譲る
T4
output=bの出力(プライマリメモリへの書き込み)username=bは5000ミリ秒休止し、CPUを譲る
T5
ユーザbのブラウザにaスレッドのusernameの値が出力され、aスレッドは終了する.
T6
ユーザbのブラウザにbスレッドのusernameの値が出力され、bスレッドが終了する.
図4サーブレット例のスレッドスケジューリング状況
図4から明らかなように、bスレッドによるインスタンス変数outputの変更は、aスレッドによるインスタンス変数outputの変更を上書きするため、ユーザaの情報がユーザbのブラウザに表示される.aスレッドが出力文を実行する場合、bスレッドによるoutputの変更がホストメモリにリフレッシュされていない場合、図2に示す出力結果は現れないため、これは偶然の現象にすぎないが、プログラムの潜在的な危険性をさらに増大させる.
スレッドの安全なサーブレットの設計
以上の解析から,インスタンス変数の不正な使用がサーブレットスレッドの不安全をもたらす主な原因であることが分かった.以下では,この問題に対して3つの解決策を示し,スキームの選択にいくつかの参考となる提案を示した.
1、SingleThreadModelインタフェースを実現する
このインタフェースは、システムが同じサーブレットに対する呼び出しをどのように処理するかを指定します.1つのサーブレットがこのインタフェースで指定されている場合、このサーブレットのサービスメソッドでは2つのスレッドが同時に実行されることはありません.もちろん、スレッドセキュリティの問題もありません.このメソッドは、前のConcurrent Testクラスのクラスヘッダ定義を次のように変更します.
Public class Concurrent Test extends HttpServlet implements SingleThreadModel {
…………
}
2、共有データに対する操作を同期する
synchronizedキーワードを使用すると、保護されたセグメントに一度に1つのスレッドしかアクセスできないことが保証され、本論文のサーブレットはブロック操作を同期することでスレッドの安全を保証することができる.同期後のコードは次のとおりです.
…………
Public class Concurrent Test extends HttpServlet { …………
Username = request.getParameter ("username");
Synchronized (this){
Output = response.getWriter ();
Try {
Thread. Sleep (5000);
} Catch (Interrupted Exception e){}
output.println(" :"+Username+"<BR>");
}
}
}
3、インスタンス変数の使用を避ける
このインスタンスのスレッドセキュリティの問題は、インスタンス変数によって発生します.サーブレット内のメソッドでインスタンス変数が使用されない限り、そのサーブレットはスレッドセキュリティです.
上記のサーブレットコードを修正し、インスタンス変数をローカル変数に変更して同様の機能を実現します.コードは以下の通りです.
……
Public class Concurrent Test extends HttpServlet {public void service (HttpServletRequest request, HttpServletResponse
Response) throws ServletException, IOException {
Print Writer output;
String username;
Response.setContentType ("text/html; charset=gb2312");
……
}
}
上記の3つの方法をテストし,いずれもスレッドセキュリティのサーブレットプログラムを設計できることを示した.しかし、サーブレットがSingleThreadModelインタフェースを実装すると、サーブレットエンジンは新しいリクエストごとに個別のサーブレットインスタンスを作成し、多くのシステムオーバーヘッドを引き起こすことになります.SingleThreadModelはサーブレット2にあります.4ではもう使用を提唱していない.同様に、プログラムで同期を使用して使用する共有データを保護すると、システムのパフォーマンスが大幅に低下します.これは、同期されたコードブロックが同じ時点で1つのスレッドしか実行できないため、顧客要求を同時に処理するスループットが低下し、多くの顧客がブロックされているためである.また、プライマリ・ストレージ・コンテンツとスレッドのワークメモリ内のデータの一貫性を確保するために、キャッシュを頻繁にリフレッシュすることは、システムのパフォーマンスにも大きく影響します.従って、実際の開発においても、サーブレット内の同期コードを回避または最小化しなければならない.Serletでインスタンス変数を使用しないことは、サーブレットスレッドのセキュリティを保証するための最良の選択です.Javaメモリモデルからも分かるように、メソッドの一時変数はスタックに空間を割り当て、各スレッドには独自のプライベートスタック空間があるため、スレッドのセキュリティに影響しません.
小結
サーブレットのスレッドセキュリティの問題は、大量の同時アクセス時にのみ発生し、発見しにくいため、サーブレットプログラムを作成する際に特に注意してください.スレッドセキュリティの問題は主にインスタンス変数に起因するため、サーブレットではインスタンス変数の使用を避ける必要があります.アプリケーション設計でインスタンス変数の使用を回避できない場合は、使用するインスタンス変数を保護するために同期を使用しますが、システムの最適なパフォーマンスを保証するために、可用性が最も低いコードパスを同期する必要があります.
原文出典:http://www.feeten.cn/html/jsp/01/20090102181631103402.html
作者:佚名