调试异步代码
调试异步代码是一项挑战,因为任务通常在一个线程中调度,而在另一个线程中执行。 每个线程都有自己的堆栈跟踪,这使得很难弄清楚线程启动之前发生了什么。
IntelliJ IDEA 通过在不同线程中的帧之间建立连接来简化操作。 这使您能够从一个工作线程回溯到任务被调度的位置,并像在同一线程中执行一样调试程序。
要尝试异步堆栈跟踪,请调试以下示例:
当我们在 printNum() 方法中的断点处停止时,我们有两个可用的堆栈跟踪:
当前线程(worker)
主线程(任务被调度的地方)

控制台中的异步堆栈跟踪
默认情况下,当您调试代码或运行 JUnit/TestNG 单元测试时,异步堆栈跟踪会显示在控制台中。

您可以通过清除相应运行配置中的 打印异常的异步堆栈跟踪 复选框来禁用测试的异步堆栈跟踪。

要为调试会话启用或禁用异步堆栈跟踪,请使用 插装代理 选项,在 中进行设置。
异步注解
异步堆栈跟踪开箱即用地支持 Swing 和 Java 并发 API,但也可以手动扩展以支持您自己的自定义类。 这是通过使用特殊注释完成的。
注解用于定义捕获和插入点:
捕获点是一种捕获堆栈跟踪的方法。 在 capture points,堆栈跟踪会被存储并分配一个键。 捕获点用
@Async.Schedule注释标记。插入点是一种方法,将以前存储的堆栈跟踪之一附加到当前堆栈。 插入点用
@Async.Execute注释标记。一个 key是一个参数或对象引用,用作捕获的堆栈跟踪的唯一标识符。 它用于匹配异步堆栈跟踪的各部分。
定义捕获和插入点
您可以注释方法或它们的参数:
如果您希望将对象引用 (
this) 用作键,请对方法本身进行注解,例如:@Async.Schedule private static void schedule(Integer i) { System.out.println("Scheduling " + i); queue.put(i); }如果您希望将参数值用作键,请注释方法参数,例如:
private static void schedule(@Async.Schedule Integer i) { System.out.println("Scheduling " + i); queue.put(i); }
要测试注解的工作方式,您可以使用以下示例:
定义自定义注解
如果您不想为项目添加 JetBrains 注解依赖项 ,您可以定义自己的注解并使用它们代替默认注解。
为捕获点和插入点创建您自己的注解。 参考请参见 Async.java。
按 Ctrl+Alt+S 打开设置,然后选择 。
点击 配置注解。
在 异步注解配置 对话框中,点击
将您的自定义注解添加到 异步调度注解 和 异步执行注解。
高级配置
基于注解的方法依赖于 instrumenting agent,并且在大多数情况下都有效。 有一种方法可以将所有工作完全委托给调试器。 这可能是必需的,如果您:
需要捕获局部变量
无法使用注解
无法使用 instrumenting agent
在底层,此方法使用隐藏断点代替注解。 当到达这样的隐藏断点时,会评估指定的表达式,其结果随后用于匹配异步堆栈跟踪的各部分。
灵活性和性能之间存在权衡。 此选项不推荐用于性能至关重要的高并发项目。
按 Ctrl+Alt+S 打开设置,然后选择 。
点击
并提供以下信息:
捕获类名 :捕获堆栈跟踪的类的全限定名,例如,
javax.swing.SwingUtilities捕获方法名 :没有参数列表和括号的方法名称,例如,
invokeLater捕获关键表达式 :其结果将用作键的表达式。 在表达式中,您可以使用框架上下文中所有可访问的内容。 方法参数可以指定为
param_N,其中N是参数的从零开始的编号,例如,param_0。插入类名 :应匹配堆栈跟踪的类的完全限定名称,例如,
java.awt.event.InvocationEvent插入方法名称 :没有参数列表和括号的方法名称,例如,
dispatch插入关键表达式 :其结果将用作键的表达式。 在表达式中,您可以使用框架上下文中所有可访问的内容。 方法参数可以指定为
param_N,其中N是参数的从零开始的编号,例如,param_0。
(可选)如果您还想捕获局部变量(原始类型和 String 值以及调用堆栈,请选择 捕获局部变量 选项。 请注意,这可能会减慢调试过程。
您可以从以下存储库下载其他捕获设置: IntelliJ IDEA debugger Capture Points
在远程 JVM 中查看异步堆栈跟踪
如果您正在调试一个远程进程,例如在 Docker 容器中管理的进程,您仍然可以使用 JVM Instrumenting Agent 来显示 Async Stack Traces,就像从 IDE 启动一样。
若要远程使用该代理,请执行以下操作:
将 <IDEA installation folder>/lib/rt/debugger-agent.jar 复制到远程机器上的任意位置
将
-javaagent:<path to debugger-agent.jar>添加到远程 JVM 选项