这是我试图做的一个愚蠢的玩具示例。
我有一个 WebSocket 类,它实现了方法onClose()
。当套接字关闭时,它会触发事件处理程序(lambda 函数(。
public class WebSocket {
ICallback cb;
public WebSocket(ICallback cb) {
this.cb = cb;
this.onClose(); // Simulating socket closing
}
public void onClose() {
this.cb.handle();
}
@FunctionalInterface
public static interface ICallback {
public abstract void handle();
}
}
在父类中,我创建了此类的新实例。当套接字关闭时,我必须重新创建此类的新实例。 (旧的无法使用(
这是我的实现
class Parent {
static WebSocket socket;
public static void main(String[] args) {
Parent.createSocket();
}
public static void createSocket() {
Parent.socket = new WebSocket(() -> { Parent.createSocket(); });
}
}
此代码有效
但是,每当触发onClose()
时,都会向堆栈添加一个新帧。如果让其运行足够长的时间并假设许多错误导致套接字关闭,则最终会导致StackOverflowError
。
运行示例
在不增加堆栈大小的情况下重新创建WebSocket
对象的最佳模式是什么?
运行持续检查套接字状态的线程是否是一种可行的方法? 即:
while(socket.isOpen()) {
Thread.sleep(1000);
}
createSocket();
堆栈溢出的原因是您的回调永远不会完成 - 您只需创建一个新套接字并运行它,但旧套接字保持活动状态。下一个也一样,依此类推。
因此,处理此问题的一种方法是同时创建套接字,以便为onClose()
方法提供实际终止的方法。
你可以通过将实际的创建提交到一个ExecutorService
来做到这一点;单线程的很好,因为毕竟,你只希望在给定的时间运行一个套接字。
class Parent {
static WebSocket socket;
// this is where the socket runs
private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
createSocket();
}
private static void createSocket() {
Parent.socket = new WebSocket(() -> {
EXECUTOR.submit(Parent::createSocket);
});
}
}
这样,当回调被onClose()
调用时,它会立即返回,并提交创建新的 Web 套接字。该创建尚未发生,因为执行器是单线程的,并且以前的套接字仍在该线程中运行。但是一旦完成,就会执行下一个提交 - 即刚刚提交的创建。
作为旁注,同时运行网络连接几乎总是一个好主意,因此主线程仍可用于其他任务。