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 内存存储 - 安全吗?

编辑:

那么这样的代码会解决这个问题吗:

/*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

4 回答 4

13

Spring 框架不会在后台对单例 bean 的多线程行为做任何事情。处理单例 bean 的并发问题和线程安全是开发人员的责任。

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

于 2013-03-13T21:27:03.597 回答
3

您本可以要求澄清我最初的回答。Spring 不会同步对 bean 的访问。如果您在默认范围(单例)中有一个 bean,则该 bean 将只有一个对象,所有并发请求都将访问该对象,要求该对象对线程安全。

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

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

private volatile List<Accounts> accounts;
于 2013-03-13T21:31:12.940 回答
1

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

于 2013-03-13T21:20:15.647 回答
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();
   }
}
于 2013-03-13T21:45:20.303 回答