zoukankan      html  css  js  c++  java
  • springbank 开发日志 一次因为多线程问题导致的applicationContext.getBean()阻塞

    几天前遇到的这个问题。由于交易是配置的,不同的交易是同一个类的不同实例,所以不可能提前将其以@autowired类似的方式注入到需要的类中

    <op:transaction id="Recharge" template="TransTemplate"></op:transaction>
    <op:transaction id="QueryAgreementAcct" template="TransTemplate"></op:transaction>

    只能写一个工具类,实现ApplicationContextAware,取得容器,然后收到交易报文的时候,根据报文里的交易名,去取得容器中对应的transaction bean。然后根据bean的template等等信息,往下执行。

    但是当我在做这件事情的时候,遇到一个奇怪的问题。这个问题我在cnblog上问过(https://q.cnblogs.com/q/95168/)。问了之后这段时间在准备一场面试,唉唉,失败了。所以今天才回过头来看这个问题:

    public class TcpServer implements ApplicationContextAware{
        private OpenbankExecutor executor;
        private int port;
        private PacketHandler packetHandler;
        private ServerSocket serverSocket;
        private Socket socket;
        private ApplicationContext applicationContext;
        private final Logger log = LoggerFactory.getLogger(getClass());
        public void init() throws IOException {
            serverSocket = new ServerSocket(port);
            log.debug("TcpServer 成功启动");
            while(true) {
                socket = serverSocket.accept();
                System.out.println("收到socket请求");
                executor.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            Map context = packetHandler.handle(socket.getInputStream());
                            String tranCode = (String) context.get("tranCode");
                            Assert.hasText(tranCode);
                            Transaction transaction = (Transaction) applicationContext.getBean(tranCode);
                            Template template = (Template) applicationContext.getBean(transaction.getTemplate());
                            log.debug("transaction: "+transaction.getId());
                            log.debug("transaction: "+template.getId());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    这个TcpServer 类也是Spring容器管理的:

    <bean id="tcpServer" class="com.openbank.portal.server.TcpServer" init-method="init">
      <property name="executor" ref="openbankThreadPool"/>
      <property name="port" value="${tcp.port}"/>
      <property name="packetHandler" ref="xmlpacketHandler"/>
    </bean>

    结果我测了一下,收到交易报文之后,走到上面代码红色的地方就“卡”住了。就像程序执行完了一样,但是没有打印出后面的debug信息,纠结了一段时间没有搞懂为什么。

    今天我把spring的源代码下载下来了,DEBUG了一下,发现好像是锁的问题,因为走到了一个synchronized方法就没有后文了,可见是一直没有获取到锁

    然后我又写了一些测试,把Spring启动期间所有被实例化的Singleton全部打印出来看,最后才慢慢搞明白

    原来就是线程的问题。

    因为啊因为啊,我上面代码里面可以看到,socket = serverSocket.accept();

    这一段,是直接在TcpServer被初始化时就要运行的,但是这里阻塞的,一直等待报文的到来。就是这样,导致spring加载bean的过程,加载到这个bean也就卡住了,甚至后面还有bean根本没有机会得到实例化。

    解决方式很简单,我把socket = serverSocket.accept(); 这个放到一个新开的线程里面取处理就好了。

  • 相关阅读:
    GJM :用JIRA管理你的项目(三)基于LDAP用户管理 [转载]
    GJM :用JIRA管理你的项目(二)JIRA语言包支持及插件支持 [转载]
    GJM : 用JIRA管理你的项目(一)JIRA环境搭建 [转载]
    javascripct导图
    Sublime Text 中使用Git插件连接GitHub
    使用webstorm上传代码到github
    jQuery.ajax()的一些例子
    jQuery.ajax()
    prefix和unprefix
    MVC , MVP , MVVM【转 阮一峰的网络日志】
  • 原文地址:https://www.cnblogs.com/heben/p/7138617.html
Copyright © 2011-2022 走看看