Threads in Spring

来源:互联网 发布:批发建筑材料的软件 编辑:程序博客网 时间:2024/05/16 05:26

使用Spring时经常会问,我们定义的Bean应该是Singleton还是Prototype?多个客户端同时调用Dao层,需要考虑线程安全吗?通过阅读官方文档和Spring的源代码,这类问题的答案是:自定义的Stateless Bean是不需要考虑线程安全问题的,可以在配置时设置为Singleton,减少new操作,提高程序效率;自定义的Stateful Bean是需要考虑线程安全问题的,Spring没有提供任何安全机制,只能由开发人员自己处理,比如使用ThreadLocal的方式或在配置时设置为Prototype。对于Dao层,Spring框架做了特殊处理。DataSource声明为Singleton模式,但其中的Connections是由支持线程安全的集合保存。与此同时,EntityManagerFactory是线程安全的,EntityManager不是,所以在每个Dao函数中都会首先执行:EntityManager entityManager = entityManagerFactory.createEntityManager()。如果追溯到最基本的JdbcTemplate用法,其官网的实例代码是:baiduxkjdsbqg

复制代码
public class JdbcCorporateEventDao implements CorporateEventDao {    private JdbcTemplate jdbcTemplate;    public void setDataSource(DataSource dataSource) {        this.jdbcTemplate = new JdbcTemplate(dataSource);    }}
复制代码

这表明每一个客户端调用都会产生一个新的JdbcTemplate对象,所以JdbcCorporateEventDao也就不会出现线程安全问题了。下面的代码是一个自定义的有状态Bean。程序运行结果表明,两个线程都在改变它的状态(成员变量),并且这种影响是不确定的。

复制代码
package com.mmh.main;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.mmh.printer.PrintHelper;public class Application {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext(                "appContext.xml");        ExecutorService executors = Executors.newCachedThreadPool();        executors.execute(new PrintThread(context));        executors.execute(new PrintThread(context));    }}class PrintThread implements Runnable {    private ApplicationContext context;    public PrintThread(ApplicationContext context) {        this.context = context;    }    public void run() {        PrintHelper printHelper = context.getBean(PrintHelper.class);                try {            Thread.sleep(10);        } catch (InterruptedException e) {            e.printStackTrace();        }                for (int i = 0; i < 2; i++) {            printHelper.print();            printHelper.increamentYears();        }    }}// 输出结果:/* * Thread[pool-1-thread-1,5,main]: yzp---28 * Thread[pool-1-thread-2,5,main]: yzp---28 * Thread[pool-1-thread-2,5,main]: yzp---30 * Thread[pool-1-thread-1,5,main]: yzp---29 */
复制代码

  把自定义Bean的scope设定为:prototype,程序运行结果表明,它的状态没有被两个线程相互干扰。

复制代码
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="printHelper" class="com.mmh.printer.PrintHelper" scope="prototype">        <constructor-arg index="0" value="yzp"/>        <constructor-arg index="1" value="28"/>    </bean></beans>
复制代码
复制代码
// 输出结果:/* * Thread[pool-1-thread-1,5,main]: yzp---28 * Thread[pool-1-thread-1,5,main]: yzp---29 * Thread[pool-1-thread-2,5,main]: yzp---28 * Thread[pool-1-thread-2,5,main]: yzp---29 */
复制代码

  在debug时跟踪代码可以看到,IoC容器初始化时会创建一个注册类DefaultSingletonBeanRegistry,在这个类里有这样一行代码:private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64),其中的singletonObjects就是保存SingletonBean的集合,它的类型是ConcurrentHashMap,该集合本身支持线程安全。下面的代码是Spring的源代码,getBean()获取SingletonBean的核心内容。

复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {        Object singletonObject = this.singletonObjects.get(beanName);        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {            synchronized (this.singletonObjects) {                singletonObject = this.earlySingletonObjects.get(beanName);                if (singletonObject == null && allowEarlyReference) {                    ObjectFactory singletonFactory = this.singletonFactories.get(beanName);                    if (singletonFactory != null) {                        singletonObject = singletonFactory.getObject();                        this.earlySingletonObjects.put(beanName, singletonObject);                        this.singletonFactories.remove(beanName);                    }                }            }        }        return (singletonObject != NULL_OBJECT ? singletonObject : null);    }
复制代码

如果singletonObjects中有需要的Bean,就直接取出来返回,如果没有,那么在同步块中使用ObjectFactory创建一个新的Bean。由代码可以看到,无论是直接获取还是新创建,操作过程都是线程安全的。

原创粉丝点击