在我们的spring框架中,我们可以使用scope来设置bean的类型。当scope写为singleton时就是单例模式,写为prototype时就是多例模式。
<bean id="test" class="com.jz.Test" scope="singleton" />
<bean id="test" class="com.jz.Test" scope="prototype" />
(scope的值还包括了request、session 等,这边使用这两作为介绍)
单例Bean与多例Bean
补充一个概念:
- 有状态的bean就是有数据存储功能。
- 无状态的bean就是不会保存数据。
singleton表示该bean全局只有一个实例,Spring中bean的scope默认也是singleton。
prototype表示该bean在每次被注入的时候,都要重新创建一个实例。
如果有状态的Bean配置为singleton,便会出现以下情况:用户a想要修改有状态bean中保存的值,但是这时用户b也在修改bean中保存的值,b完成修改后a提交他修改好的值。此时b的操作就被a完全覆盖了。
如果使用的prototype的方式,每次注入都产生一个实例。就不存在数据共享的情况,也就不存在线程安全的问题。
Spring中的单例与设计模式里面的单例略有不同,设计模式的单例是在整个应用中只有一个实例,而Spring中的单例是在一个IOC容器中就只有一个实例。
总结,对于有状态的bean我们要采用prototype的方式以保证线程安全,对于无状态的bean我们可以采取singleton的方式。
在Spring中如何保证成员变量线程安全
Spring作为一个IOC容器,帮助我们管理了许许多多的bean。但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。
我们可以使用treadlocal的方式来解决线程安全的问题:
- 每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
- 将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦
下面我们创建一个有状态的bean:
public class TopicDao {
//①一个非线程安全的变量
private Connection conn;
//②引用非线程安全变量
public void addTopic() {
Statement stat = conn.createStatement();
}
}
当我们需要多个线程访问这个成员变量的时候可以使用threadlocl对其进行改造,使每个线程获得都访问他们自己的成员变量。
public class TopicDao {
//①使用ThreadLocal保存Connection变量
private static ThreadLocal <Connection>connThreadLocal = newThreadLocal<Connection>();
public static Connection getConnection() {
// ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
// 并将其保存到线程本地变量中。
if (connThreadLocal.get() == null) {
Connection conn = getConnection();
connThreadLocal.set(conn);
return conn;
}
// ③直接返回线程本地变量
return connThreadLocal.get();
}
public void addTopic() {
// ④从ThreadLocal中获取线程对应的Connection
try {
Statement stat = getConnection().createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
不同的线程在使用TopicDao时,先判断connThreadLocal是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。