Spring Singleton螺纹安全

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

public AccountDao { private NamedParameterJdbcTemplate njt; private List 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(); } 

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

我建议阅读下面的文章: Spring Singleton,Request,Session Beans和Thread Safety

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

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

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

 private volatile List accounts; 

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

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

 public AccountDao { private static List accounts; private static List 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(); } }