Java 如何修复';此操作可能是死锁原因';

Java 如何修复';此操作可能是死锁原因';,java,discord,deadlock,Java,Discord,Deadlock,当我第二次调用sendDM()时,会收到死锁警告。我怎样才能解决这个问题 我正在使用JDAAPI,并试图获取与RestAction.complete()一起发送的消息 private void start(){ sendDM(getP1().getUser()); sendDM(getP2().getUser()); EmbeddeBuilder embeddeBuilder=新的EmbeddeBuilder(); textChannel.sendMessage(p1.getUser().Get

当我第二次调用sendDM()时,会收到死锁警告。我怎样才能解决这个问题

我正在使用JDAAPI,并试图获取与RestAction.complete()一起发送的消息

private void start(){
sendDM(getP1().getUser());
sendDM(getP2().getUser());
EmbeddeBuilder embeddeBuilder=新的EmbeddeBuilder();
textChannel.sendMessage(p1.getUser().GetAsMotion()+“”+p2.getUser().GetAsMotion()).queue();
textChannel.sendMessage(embedBuilder.setTitle(“**匹配已开始-#”+getGameId()+“**”)
.addField(“信息”,“ID:+getGameId()+”\n映射:7189-0031-5500“+”\n状态:+getStatus().toString(),true)
.addField(“播放器1”,p1.getUser().GetAsMotion()+“-Host”,false)
.addField(“播放器2”,p2.getUser().GetAsMotion(),false)
.addField(“大厅”,textChannel.getasmotion(),false)
.setFooter(“两个参与者都将收到一条私人消息,可以在其中提交匹配结果”,null).build()
.queue();
}
私有void sendDM(用户){
EmbeddeBuilder embeddeBuilder=新的EmbeddeBuilder();
user.openPrivateChannel().queue((通道)->
{
channel.sendMessage(p1.getUser().GetAsMotion()+“”+p2.getUser().GetAsMotion()).queue();
Message Message=channel.sendMessage(embedBuilder.setTitle(“**匹配已开始-#“+getGameId()+“**”)
.addField(“信息”,“ID:+getGameId()+”\n映射:7189-0031-5500“+”\n状态:+getStatus().toString(),true)
.addField(“播放器1”,p1.getUser().GetAsMotion()+“-Host”,false)
.addField(“播放器2”,p2.getUser().GetAsMotion(),false)
.addField(“大厅”,textChannel.getasmotion(),false)
.addField(“**如果您赢了**”,则“:白色复选标记:”,true)
.addField(“**如果您松开**”,则“:x:”,true)
.setFooter(“两个参与者都将收到一条私人消息,可以在其中提交匹配结果”,null).build()
.complete();
message.addReaction(“\u274C”).complete();
message.addReaction(“\u2705”).complete();
});
}
我希望它只返回消息,但我收到以下错误:

[ForkJoinPool.commonPool-worker-1] ERROR net.dv8tion.jda.api.requests.RestAction - Encountered error while processing success consumer
java.lang.IllegalStateException: Preventing use of complete() in callback threads! This operation can be a deadlock cause
    at net.dv8tion.jda.internal.requests.RestActionImpl.complete(RestActionImpl.java:187)
    at net.dv8tion.jda.api.requests.RestAction.complete(RestAction.java:357)
    at match.Match.lambda$sendDM$0(Match.java:71)
    at net.dv8tion.jda.api.requests.Request.lambda$onSuccess$0(Request.java:83)
    at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)


只有一个回调线程用于运行回调*

您的调用
user.openPrivateChannel().queue((通道)->{…}
在这个回调线程中运行

您的调用
message.addReaction(“\u274C”).complete();
会一直阻塞直到它完成,但它会在同一个回调线程上运行,该线程已经在忙于运行要运行
complete()的外部回调
…您看到了问题所在。这是在后台检测到的,会引发异常以防止发生此死锁

只需使用
message.addReaction(“\u274C”).queue();
queue()
立即返回,以便回调线程在外部回调完成后有机会运行它

有关和,请参阅文档


*请参阅以下疑难解答文档:

因为我们决定使用单线程池(1),所以我们只有一个线程来执行回调。这个线程被第一个回调(2)使用,不能用于第二个回调(3)

由于这个原因,我们根本不允许在任何回调线程中使用
complete()
。如果使用回调,则应使用
queue()

(版权所有2015-2019年奥斯汀·基纳、迈克尔·里特、弗洛里安·斯皮尔)


您应该决定只使用阻塞还是异步。我建议保持异步并使用以避免嵌套回调:

user.openprivatechnel().submit()//CompletableFuture
.然后撰写((频道)->{
channel.sendMessage(已忽略).queue();
返回channel.sendMessage(embed.submit();//CompletableFuture
})
.thenCompose((消息)->CompletableFuture.allOf(//将2个期货组合为1
message.addReaction(a).submit(),
message.addReaction(b.submit())//CompletableFuture
.完成时((v,错误)->{
if(error!=null)error.printStackTrace();//处理错误
});
正如Max Vollmer正确指出的那样,您不能在
queue()
回调内部使用
complete()
,因为它会使回调线程死锁。
complete()
的工作原理是在处理回调的同一线程上运行,该线程当前正忙着等待完整的调用完成,而该线程正在等待队列回调完成,这是一个永远不会完成的无尽循环