• Threads in Spring


    使用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用法,其官网的实例代码是:

     
    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。由代码可以看到,无论是直接获取还是新创建,操作过程都是线程安全的。

  • 相关阅读:
    倒序三角形
    有时间了
    测试一个数是不是素数
    初学C++编写小程序
    N!的递归调用
    1+...+5的递归调用
    1!+....+5!
    菱形块
    23.git简单使用
    27.flask学习
  • 原文地址:https://www.cnblogs.com/xsmhero/p/Java.html
Copyright © 2020-2023  润新知