2013-03-13 4 views
8

私はその下に定義されたJavaクラスは、依存性注入を経由して自分のWebアプリケーションに注入されている場合:春シングルトンスレッドセーフ

public AccountDao 
{ 
    private NamedParameterJdbcTemplate njt; 
    private List<Account> accounts; 

    public AccountDao(Datasource ds) 
    { 
     this.njt = new NamedParameterJdbcTemplate(ds); 
     refreshAccounts(); 
    } 

    /*called at creation, and then via API calls to inform service new users have 
    been added to the database by a separate program*/ 
    public void refreshAccounts() 
    { 
     this.accounts = /*call to database to get list of accounts*/ 
    } 

    //called by every request to web service 
    public boolean isActiveAccount(String accountId) 
    { 
     Account a = map.get(accountId); 
     return a == null ? false : a.isActive(); 
    } 
} 

私は、スレッドの安全性が心配です。 Springフレームワークは、あるリクエストがリストから読み込み中で、現在別のリクエストが別のリクエストによって更新されているケースを処理しませんか?以前は他のアプリケーションで読み書きロックを使用していましたが、これまでにないようなケースについては考えていませんでした。

Beanをシングルトンとして使用する予定だったので、データベースの負荷を軽減できました。

Java Memory Storage to Reduce Database Load - Safe?

EDIT:

だから、この問題を解決するため、次のようにコーディングします。

/*called at creation, and then via API calls to inform service new users have 
     been added to the database by a separate program*/ 
     public void refreshAccounts() 
     { 
      //java.util.concurrent.locks.Lock 
      final Lock w = lock.writeLock(); 
      w.lock(); 
      try{ 
       this.accounts = /*call to database to get list of accounts*/ 
      } 
      finally{ 
      w.unlock(); 
      } 
     } 

     //called by every request to web service 
     public boolean isActiveAccount(String accountId) 
     { 
      final Lock r = lock.readLock(); 
      r.lock(); 

      try{ 
       Account a = map.get(accountId); 
      } 
      finally{ 
       r.unlock(); 
      } 
      return a == null ? false : a.isActive(); 
     } 

答えて

13

シングルトンBeanのマルチスレッド動作に関して、Springフレームワークは何もしません。シングルトンBeanの並行性の問題とスレッドの安全性を処理するのは開発者の責任です。

私は以下の記事を読んでお勧めします:Spring Singleton, Request, Session Beans and Thread Safety

1

としてところで

、これは以下の質問のフォローアップでありますシングルトンで非同期の場合、Springは任意の数のスレッドが同時に呼び出すことを許可しますisActiveAccountrefreshAccounts。したがって、このクラスはスレッドセーフではなく、データベースの負荷を軽減しません。

+0

このJavaクラス(またはアプリケーションコンテキストフィックス)に含まれるコードを介してこの修正プログラムを簡単に使用できますか、キャッシュ/データベースソリューションをお勧めしますか? – thatidiotguy

+0

あなたができることは、 'refreshAccounts()'のデータベースを呼び出すために一時的なリストを使うことです。それが返ってくると、 'accounts'に同期して、それをそのリストに再割り当てします。 –

+0

私は間違いなくキャッシュ/データベースと言うでしょう。同時実行性を自分で管理するのは難しいです。キャッシングを使用すると、少なくとも並行性の制御を覚えることができます。任意の数のリクエストが本当に必要な場合は、スコープ=プロトタイプを宣言します。次に、あなたが懸念していた負荷の問題にぶつかります。 –

2

あなたは私のinitial answer上の明確化を求めている可能性があります。 SpringはBeanへのアクセスを同期しません。デフォルトのスコープ(シングルトン)にBeanがある場合、そのBeanに対して1つのオブジェクトのみが存在し、すべての並行要求がそのオブジェクトにアクセスし、そのオブジェクトをスレッドセーフにする必要があります。

ほとんどの春の豆は変更可能な状態を持たないため、スレッドセーフです。あなたのbeanは可変状態を持っているので、他のスレッドが現在組み立てているアカウントのリストをスレッドが見ないようにする必要があります。

これを行う最も簡単な方法は、アカウントフィールドをvolatileにすることです。これは、(あなたがしているように見えるように)フィールドを埋めた後に新しいリストをフィールドに割り当てることを前提としています。

private volatile List<Accounts> accounts; 
+0

申し訳ありませんが、私はそれがコメントでそれに入るために他の質問のタイトルに真実ではないと感じました。それは本当に別の問題です。揮発性のソリューションは私が上に置いた編集コードとどのように似ていますか? – thatidiotguy

+0

よりシンプルで、待ち時間がなく、おそらく明示的なロックよりも少し効率的です(データベースとの入出力と比較するとその差はごくわずかですが)。 – meriton

0

私たちは多くのそのようなメタデータを持ち、いくつかの11ノードが動作しています。各アプリケーションノードには、そのようなデータの静的マップがあります。したがって、その1つのインスタンスのみ、毎日のピーク時に1回起動するDBからの起動、またはサポート担当者がトリガーしたときです。リアルタイムで更新が必要なデータの一部については、1ノードから他のノードに更新を送信するための簡単なhttpポストベースのAPIがあります。

public AccountDao 
{ 
    private static List<Account> accounts; 
    private static List<String> activeAccounts; 
    private NamedParameterJdbcTemplate njt; 

    static { 
     try{ 
     refreshAccounts(); 
     }catch(Exception e){ 
     //log but do not throw. any uncaught exceptions in static means your class is un-usable 
     } 
    } 


    public AccountDao(Datasource ds) 
    { 
     this.njt = new NamedParameterJdbcTemplate(ds); 
     //refreshAccounts(); 
    } 

    /*called at creation, and then via API calls to inform service new users have 
    been added to the database by a separate program*/ 
    public void refreshAccounts() 
    { 
     this.accounts = /*call to database to get list of accounts*/ 
    } 

    public void addAccount(Account acEditedOrAdded) 
    { 
     //add or reove from map onr row 
     //can be called from this node or other node 
     //meaning if you have 2 nodes, keep IP port of each or use a internal web service or the like to tell 
     //node B when a account id added or changed in node A ... 
    } 

    //called by every request to web service 
    public static boolean isActiveAccount(String accountId) 
    { 
     Account a = map.get(accountId); 
     return a == null ? false : a.isActive(); 
    } 
}