Spring beanとスレッドセキュリティ

8055 ワード

Springフレームワークを使用する場合、マルチスレッドの問題を知らなかったり無視したりすることが多い.プログラムを書くときやユニットテストをするとき、マルチスレッドの問題に遭遇する機会は難しいので、マルチスレッドテストをシミュレートする環境はそんなに簡単ではありません.しかし、潜在的な脆弱性を考えなければ、プログラムのステルス殺し屋になり、あなたが知らないうちに爆発します.また,通常はプログラム配信使用時に,生産環境下でトリガーされるのは面倒なことである.
Springフレームワークを使用する場合、マルチスレッドの問題を知らなかったり無視したりすることが多い.プログラムを書くときやユニットテストをするとき、マルチスレッドの問題に遭遇する機会は難しいので、マルチスレッドテストをシミュレートする環境はそんなに簡単ではありません.しかし、潜在的な脆弱性を考えなければ、プログラムのステルス殺し屋になり、あなたが知らないうちに爆発します.また,通常はプログラム配信使用時に,生産環境下でトリガーされるのは面倒なことである.
 
ではSpring Beanでは、ほとんどの場合、オブジェクトインスタンス(Object)とメソッドはスレッドが安全ですか?
これはBeanの生命管理と範囲(Scope)をどのように定義するかによって決まりますが、多くの場合、Spring Beanは特に注意して方法を取らなければ安全ではありません.Spring Beanの生成はコンテナによって実現され,逆制御IoCを用いた場合,注入されたオブジェクトインスタンスも他のスレッドと共有されるためである.
 
たとえば
public class PersonController{    
    private PersonService personService;    
    
    public void setFirstName(HttpServletRequest request){                    
        personService.setFirstName(request.getParameter("firstname"));    
    }    
    
    public String getFirstName(){             
        return   personService.getFirstName();    
    }
                 
}

PersonControllerにアクセスするスレッドが2つある場合、1つはsetFrostNameを呼び出してfirstnameを設定し、もう1つはgetFirstNameを呼び出して最初のスレッドに設定された値を取得します.これは、PersonControllerがデフォルトでは単一のインスタンス(Singleton)であり、personServiceも個別のインスタンスがpersonControllerに注入されるためである.この場合、マルチスレッドは、各メソッド(method)が共有するオブジェクトの属性または状態を変更しないことを保証できない限り、安全ではありません.
 
スレッドのセキュリティ方法
public class PersonController {   
 
    private PersonService personService
    
    public void setFirstName(HttpServletRequest request){        
      Person person = new Person();        
      person.setFirstName();        
      personService.savePerson(person);    
    }
 }

上記の例は、スレッドAがsetFirstNameメソッドにアクセスし、person.setFirstName()に実行されると、システムはこのスレッドを掛けることを決定し、実行Bに移行し、Bは同じことをして保存します.その後、システムは、Aスレッドに独自のスタック状態があり、それ自体が他のスレッドと共有されていないオブジェクトインスタンスまたは状態のため、保留中のAスレッドを実行する.なお、ここではpersonServices.savePerson()メソッドがスレッドセキュリティであると仮定し、そうでない場合はpersonServices.savePerson()がスレッドセキュリティであるかどうかを調べる必要がある.
したがって、ほとんどの場合、spring beanはスレッドセキュリティではありません.あるいは、オブジェクトやメソッドのスレッドセキュリティをどのように管理するかを教えてくれないと、スレッドセキュリティの問題が潜在的に発生します.
Spring beanはそのため、以下のスレッドの安全な使用可能な宣言(annotation)を提供し、実際の状況に応じてクラスまたは方法をそれぞれ定義することができます.
 
単一インスタンスsingleton(デフォルト)
Spring IoCコンテナ全体でbeanインスタンスが1つしかなく、すべてのスレッドがインスタンスを共有しています.
 
プロトタイプインスタンスprototype
リクエストのたびに新しいインスタンスが作成され、返されます.すべてのスレッドには個別のインスタンスが使用されます.これは比較的安全ですが、メモリと計算リソースが大量に消費されます.
beanのxmlプロファイルの定義
"personService" class="com.test.PersonService" scope="prototype">    

bean>
"personControoler" class="com.test.PersonController">    
    <lookup-method name="getPersonService" bean="personService"/>
bean>

抽象クラス
public abstract class PersonController {    
    public void setFirstName(HttpServletRequest request){        
        Person person = new Person();        
        person.setFirstName();        
        personService.savePerson(person);    
     }   
     
     public abstract PersonService getPersonService();
 }

ここで、クラス内のgetPersonService()メソッドが呼び出されるたびに、新しいpersonServiceインスタンスが得られます.注意が必要なのは、PersonControllerが抽象クラスになったことです.
 
リクエスト範囲インスタンスrequest 
HTTPリクエストを受信するたびに、リクエストサイクル全体で一意である一意のインスタンスが割り当てられます.
 
セッション範囲インスタンスセッション
各ユーザー・セッション・サイクルには、セッション・サイクル全体で一意であり、同じセッション範囲のすべてのリクエストがインスタンスを共有するインスタンスが割り当てられます.
 
グローバルセッション範囲インスタンスglobalsession
これは、セッション範囲インスタンスのほとんどと同じですが、portletを使用する場合、各portletには独自のセッションがあるため、1つのページに複数のportletがあり、1つのbeanを共有する必要がある場合にのみ使用されます.
 
Request範囲とSession範囲の同時性の高いMVC環境での使用
以上から分かるように、デフォルトでは@Controllerを宣言するクラスが追加されるのは単一のインスタンスであるため、クラス内のメソッドと変数のスレッドセキュリティの問題に特に注意する必要があります.
一方、同時ユーザーの環境を考慮すると、Reqestの範囲を使用することができます.たとえば、ユーザーのリクエストごとに新しいコントローラインスタンスを割り当てる必要があります.これは、通常、処理ロジックが複雑なリクエストに直面し、より多くのリソースを消費する必要があり、オンラインでチケットを予約したり、支払いをしたりする必要があります.
@Controller@Scope("request")
public class OnlinePaymentController {                 
    private OnlinePayment payment;          
    public void pay(Float amount){                    
        creditCheck(user);                    
        payment.pay(amount);                    
        ...                    
        //a lot of processes have to be done here          
    }
}

場合によっては、Session範囲を使用して、各ユーザーに対して、ユーザーの複数回の要求、例えばネットショッピングカートに対応するインスタンスを提供することを考慮することができる.
@Controller
@Scope("session")
public class ShoppingCartController {       

          private ShoppingCart shoppingCart



          public void checkIn(ShoppingItem item, Integer number){                    
              shoppingCart.add(item, number);          
          }          

          public void checkOut(){                    
              shoppingCart.sum();          
          }
 }

 
実際の本番環境では、単一インスタンスとセッションの範囲が多く使用されています.単一インスタンスのスレッドのセキュリティが保証される場合は、パフォーマンスが優先されます.結局、Webサイトのユーザー数が増加している場合でも、複数のユーザーリクエストを限られたインスタンスで処理できるのがお得です.