当我第二次调用sendDM()时,收到死锁警告。我该如何解决?
我正在使用JDA Api,并试图获取我用RestAction .complete()发送的消息
private void start(){
sendDM(getP1().getUser());
sendDM(getP2().getUser());
EmbedBuilder embedBuilder = new EmbedBuilder();
textChannel.sendMessage(p1.getUser().getAsMention() + " " + p2.getUser().getAsMention()).queue();
textChannel.sendMessage(embedBuilder.setTitle("**Match has started - #" + getGameId() + "**")
.addField("Info", "ID: " + getGameId() + "\nMap: 7189-0031-5500" + "\nStatus: " + getStatus().toString(),true)
.addField("Player 1", p1.getUser().getAsMention() + " - Host", false)
.addField("Player 2", p2.getUser().getAsMention(), false)
.addField("Lobby", textChannel.getAsMention(), false)
.setFooter("Both participants will receive a Private Message where they can submit match results", null).build())
.queue();
}
private void sendDM(User user){
EmbedBuilder embedBuilder = new EmbedBuilder();
user.openPrivateChannel().queue((channel) ->
{
channel.sendMessage(p1.getUser().getAsMention() + " " + p2.getUser().getAsMention()).queue();
Message message = channel.sendMessage(embedBuilder.setTitle("**Match has started - #" + getGameId() + "**")
.addField("Info", "ID: " + getGameId() + "\nMap: 7189-0031-5500" + "\nStatus: " + getStatus().toString(),true)
.addField("Player 1", p1.getUser().getAsMention() + " - Host", false)
.addField("Player 2", p2.getUser().getAsMention(), false)
.addField("Lobby", textChannel.getAsMention(), false)
.addField("**If you win**", ":white_check_mark:", true)
.addField("**If you loose**", ":x:", true)
.setFooter("Both participants will receive a Private Message where they can submit match results", 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)
答案 0 :(得分:1)
只有一个回调线程用于运行回调。*
您的呼叫user.openPrivateChannel().queue((channel) -> {...}
在此单个回调线程中运行。
您的调用message.addReaction("\u274C").complete();
将一直阻塞,直到完成为止,但它将在同一个回调线程上运行,该线程已经在忙于要运行complete()
的外部回调...问题。在后台可以检测到这一点,并且会抛出一个异常以防止发生这种死锁。
只需使用message.addReaction("\u274C").queue();
即可。 queue()
立即返回,以便在外部回调完成后,回调线程有机会运行它。
请参阅complete()
和queue()
的文档。
*请参阅以下故障排除文档here:
由于我们决定使用单线程池(1),因此只有一个线程可以执行回调。该线程由第一个回调(2)使用,而不能用于第二个回调(3)。
由于这个原因,我们根本根本不允许在任何回调线程中使用
complete()
。如果您使用回调,则应该使用queue()
。(版权所有2015-2019 Austin Keener,Michael Ritter和FlorianSpieß)
答案 1 :(得分:0)
您应该决定只是阻塞还是异步。我建议保持异步并使用submit()
以避免嵌套的回调:
user.openPrivateChannel().submit() // CompletableFuture<PrivateChannel>
.thenCompose((channel) -> {
channel.sendMessage(ignored).queue();
return channel.sendMessage(embed).submit(); // CompletableFuture<Message>
})
.thenCompose((message) -> CompletableFuture.allOf( // combine 2 futures to 1
message.addReaction(a).submit(),
message.addReaction(b).submit())) // CompletableFuture<Void>
.whenComplete((v, error) -> {
if (error != null) error.printStackTrace(); // handle errors
});
如Max Vollmer所正确指出的那样,您不能在complete()
回调中使用queue()
,因为它会死锁该回调的线程。 complete()
通过在用于处理当前正忙于等待完成调用(正在等待队列回调完成)的回调的同一线程上运行而工作,这是一个永无休止的循环,永远不会完成。 [1]