如何避免无限递归方法中的Stack Overflow错误?

时间:2018-03-22 19:18:10

标签: java recursion stack overflow infinite

我知道有很多关于堆栈溢出错误的帖子,我理解为什么我的具体问题正在发生,我的问题基本上是如何在这种特定情况下摆脱递归。我有一个类,它建立并维护一个客户端连接(专门用于HL7消息传递,但它本质上是一个美化的客户端连接)到另一个托管相应服务器连接的系统。这堂课'构造函数启动一个新线程并运行以下方法:

@Override
public void connect() 
{
    try
    {
        setStatus("Connecting");

        connection = context.newClient(intfc.getIp(), port, false);
        connected = true;
        setStatus("Connected");

        logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully connected " + connectionType + " client connection to " 
                + intfc.getName() + "(" + intfc.getIp() + ") on port " + port);

        monitor();
    }
    catch (HL7Exception ex)
    {
        connected = false;
        setStatus("Disconnected");

        try
        {
            TimeUnit.SECONDS.sleep(connectionRetryIntervalInSeconds);
            connect();
        }
        catch (InterruptedException ex2)
        {}
    }
}

成功连接服务器后,monitor方法只是在另一个线程中检查连接是否仍以给定间隔启动。如果它关闭,监视线程将被中断并再次调用connect()方法。

我一开始并没有预料到这一点,但您可以快速了解为什么connect()方法在运行几天后导致堆栈溢出错误。我很难想到一种方法来使相同的功能工作,而没有连接方法每次连接失败时再次调用自己。

任何建议都将不胜感激。

谢谢!

4 个答案:

答案 0 :(得分:1)

通常,您在需要时使用Stack对象来模拟递归。

但是,在你的情况下,你为什么要使用递归呢? while循环符合目的。

while(true /**or some relevant condition**/){
   try{ //try to connect
      ....
   catch(HL7Exception ex){
      //sleep
   }
 }

我不确定您申请的目的,但可能有比睡觉更好的方法。您可以使用ScheduledExecutorService,但如果它是一个有一个目的的单线程程序,则可能没必要。

答案 1 :(得分:1)

我按照建议将代码更改为迭代方法,效果很好!

@Override
public void initThread() 
{
    initConnectionEntity();
    mainThread = new Thread()
    {
        @Override
        public void run() 
        {
            while (running)
            {
                if (!connected)
                {
                    try
                    {
                        connect();
                    }
                    catch (HL7Exception ex)
                    {
                        connected = false;
                        setStatus("Disconnected");

                        try
                        {
                            TimeUnit.SECONDS.sleep(connectionRetryIntervalInSeconds);
                        }
                        catch (InterruptedException ex2)
                        {}
                    }
                }
                try
                {
                    TimeUnit.MILLISECONDS.sleep(500);
                }
                catch (InterruptedException ex2)
                {}
            }
        }
    };
    mainThread.setName(intfc.getName() + " " + connectionType + " Main Thread");
    mainThread.start();
}

@Override
public void connect() throws HL7Exception
{
    setStatus("Connecting");

    connection = context.newClient(intfc.getIp(), port, false);
    connected = true;
    setStatus("Connected");

    logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully connected " + connectionType + " client connection to " 
            + intfc.getName() + "(" + intfc.getIp() + ") on port " + port);

    monitor();
}

private void monitor()
{
    monitoringThread = new Thread()
    {
        @Override
        public void run() 
        {
            try
            {
                while (running)
                {
                    if (!connection.isOpen())
                    {
                        if (connected == true)
                        {
                            logEntryService.logWarning(LogEntry.CONNECTIVITY, "Lost " + connectionType + " connection to " 
                                    + intfc.getName() + "(" + intfc.getIp() + ") on port " + port);
                        }

                        connected = false;
                        setStatus("Disconnected");

                        monitoringThread.interrupt();
                    }
                    else
                    {
                        connected = true;
                    }
                    TimeUnit.SECONDS.sleep(connectionMonitorIntervalInSeconds);
                }
            }
            catch (InterruptedException ex)
            {
                logEntryService.logDebug(LogEntry.CONNECTIVITY, "Monitoring thread for " + connectionType 
                        + " connection to " + intfc.getName() + " interrupted");                         
            }
        }
    };
    monitoringThread.setName(intfc.getName() + " " + connectionType + " Monitoring Thread");
    monitoringThread.start();
}

答案 2 :(得分:0)

当我不得不在c#中处理这个问题时,我使用了一个Stack,并添加了新的类,而不是使用递归。然后第二个循环将检查堆栈中是否有任何需要处理的对象。这避免了堆栈溢出,否则我会有大量的递归。 Java中是否有类似的Stack集合?

答案 3 :(得分:0)

为什么你首先调用for (DataSnapshot ds : dataSnapshot.getChildren()){方法?你提到它是在一个单独的线程中启动的,那么你不能在应用程序出现时在新线程中启动它吗?然后就不会有递归调用。