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(); 這個放到一個新開的線程里面取處理就好了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM