Spring Singleton螺纹安全

时间:2013-03-13 21:14:39

标签: java multithreading spring

如果我在下面定义了一个通过依赖注入在我的Web应用程序中注入的Java类:

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?

编辑:

所以这样的代码会解决这个问题:

/*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();
       }

4 个答案:

答案 0 :(得分:13)

Spring框架没有关于单例bean的多线程行为的任何内容。开发人员有责任处理单例bean的并发问题和线程安全问题。

我建议您阅读以下文章:Spring Singleton, Request, Session Beans and Thread Safety

答案 1 :(得分:3)

您可能要求澄清我的initial answer。 Spring不会同步对bean的访问。如果在默认作用域(单例)中有一个bean,那么该bean只有一个对象,并且所有并发请求都将访问该对象,从而要求该对象保证线程安全。

大多数spring bean没有可变状态,因此非常简单。您的bean具有可变状态,因此您需要确保没有线程看到其他线程当前正在组装的帐户列表。

最简单的方法是创建帐户字段volatile。这假设您在填充后将新列表分配给字段(正如您似乎正在做的那样)。

private volatile List<Accounts> accounts;

答案 2 :(得分:1)

作为单例和非同步,Spring将允许任意数量的线程同时调用isActiveAccountrefreshAccounts。因此,这个类不会是线程安全的,也不会减少数据库负载。

答案 3 :(得分:0)

我们有很多这样的元数据,并且运行了大约11个节点。在每个应用程序节点上,我们都有这样的数据的静态映射,因此只有一个实例,每天在非高峰时段启动一次,或者当支持人员触发它时从数据库初始化。有一个简单的基于http post的内部API,可以将更新从1个节点发送到其他节点,以获取我们需要实时更新的一些数据。

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();
   }
}