【问题标题】:Spring Singleton Thread Safety弹簧单例线程安全
【发布时间】:2013-03-13 21:14:38
【问题描述】:

如果我在下面定义了一个 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?

编辑:

这样的代码能解决这个问题吗:

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

【问题讨论】:

    标签: java multithreading spring


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      private volatile List<Accounts> accounts;
      

      【讨论】:

      • 对不起,我觉得在 cmets 中讨论另一个问题的标题是不诚实的。这确实是一个单独的问题。 volatile 解决方案与我在上面放置的编辑代码相比如何?
      • 它比显式锁更简单、无需等待并且可能更高效(尽管在将其与 I/O 与数据库进行比较时,这种差异可以忽略不计)。
      【解决方案3】:

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

      【讨论】:

      • 好的,继续接受:这是否可以通过此 Java 类中包含的代码(或应用程序上下文修复)轻松修复,还是我最好选择缓存/数据库解决方案?
      • 你可以做的是使用一个临时列表来调用refreshAccounts()中的数据库。当它返回时,在 accounts 上同步并将其重新分配给该列表。
      • 我肯定会说缓存/数据库。自己管理并发是很困难的。通过缓存,您至少可以记住并发控制。如果你真的想要任意数量的请求,我会声明 scope=prototype。然后你遇到了你关心的负载问题。
      【解决方案4】:

      我们有很多这样的元数据,并且有大约 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();
         }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-03
        • 2017-07-09
        • 2018-09-08
        • 1970-01-01
        • 2017-08-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多