
本文介绍如何在 Java 中模拟 C++ 函数指针行为,利用 SAM(单抽象方法)接口(如 Runnable)和方法引用,将 tick() 和 render() 分别交由两个独立线程循环执行,避免代码重复与类膨胀。
本文介绍如何在 java 中模拟 c++ 函数指针行为,利用 sam(单抽象方法)接口(如 runnable)和方法引用,将 `tick()` 和 `render()` 分别交由两个独立线程循环执行,避免代码重复与类膨胀。
要实现“一个线程专注调用 tick()、另一个专注调用 render()”,核心思路是解耦循环逻辑与具体执行动作。Java 虽无原生函数指针,但自 JDK 8 起,通过 SAM 接口 + 方法引用(instance::method)或 Lambda 表达式,可优雅达成同等效果。
以下是一个精简、可复用的 RunnableLooper 实现:
public class RunnableLooper implements Runnable {
private final State state;
private final Runnable action; // 封装待执行的无参无返回动作(即 tick 或 render)
private final int SECOND = 1_000_000_000;
private final int FPS = Integer.parseInt(
ResourceBundle.getBundle("game_properties").getString("FPS")
);
private final long quanta = SECOND / FPS;
public RunnableLooper(State state, Runnable action) {
this.state = state;
this.action = action;
}
@Override
public void run() {
long delta = 0;
long last = System.nanoTime();
while (!Thread.currentThread().isInterrupted()) {
long now = System.nanoTime();
delta += now - last;
last = now;
if (delta >= quanta) {
delta = 0;
try {
action.run(); // ✅ 统一入口:实际执行 tick() 或 render()
} catch (RuntimeException e) {
System.err.println("Exception in Looper: " + e.getMessage());
State.setState(new MenuState()); // 安全降级
}
}
// 可选:微休眠避免空转耗尽 CPU(尤其在高 FPS 场景)
Thread.onSpinWait();
}
}
}
在启动类中,只需两行方法引用即可创建两个职责分明的线程:
public class Launcher {
public static void main(String[] args) throws IOException {
State menu = new MenuState();
State.setState(menu);
menu.init(); // 注意:init 应在状态设置后立即调用
// 创建两个 Looper:分别绑定 tick 和 render
Thread tickThread = new Thread(new RunnableLooper(menu, menu::tick));
Thread renderThread = new Thread(new RunnableLooper(menu, menu::render));
tickThread.start();
renderThread.start();
}
}
✅ 优势说明:
- 零重复逻辑:循环节拍(quanta 计算、delta 累加、异常兜底)仅维护一份;
- 类型安全 & IDE 友好:menu::tick 是编译期绑定的方法引用,支持跳转、重构与自动补全;
- 向后兼容:若使用 JDK
⚠️ 重要注意事项:
- State.getState() 是静态全局状态,在多线程下存在竞态风险。建议改用线程安全的状态管理器(如 AtomicReference
)或直接传入 State 实例(如本方案所示),避免隐式共享; - tick() 和 render() 若涉及共享资源(如游戏对象列表、渲染缓冲区),必须添加显式同步(如 synchronized、ReentrantLock 或并发集合);
- 长时间运行的 while(true) 循环应响应中断(如 Thread.interrupted() 检查),便于优雅关闭线程(生产环境务必添加 shutdown() 机制)。
总结:Java 的函数式编程能力足以替代传统函数指针。抓住「行为即对象」本质,用 Runnable 封装动作、用构造器注入依赖、用方法引用传递语义,即可写出清晰、解耦、易扩展的多线程游戏主循环架构。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











