如何在 Java 中正确执行线程回调?

Posted

技术标签:

【中文标题】如何在 Java 中正确执行线程回调?【英文标题】:How to properly do a thread callback in Java? 【发布时间】:2021-12-05 17:10:20 【问题描述】:

我正在使用 Java(非 android)开发一个项目。我有一个做一些长时间工作的线程,我希望在工作完成后调用主线程中的一个函数。我已经看到很多关于这个问题的问题,但似乎没有任何答案有效。我当前的实现如下所示:

有一个带有回调函数的接口 将该接口作为runnable中的参数 在主类中实现,然后创建runnable并启动一个新线程。 在回调函数中,打印当前线程名称

这不起作用,因为当线程完成时,它总是打印新线程的名称,而不是主线程的名称。如何使回调函数在主线程上调用,而不在新线程上调用?

谢谢!

编辑:这是我尝试过的代码:

import java.util.Random;

public class ATestThreadClass 
    public static void main(String[] args) 
        new Thread(new RandomRunnable(data -> 
        System.out.println("Called on " + Thread.currentThread().getName());
        System.out.println(data.toString());
    )).start();


    static class RandomRunnable implements Runnable 
    private final CallbackInterface callbackInterface;

    public RandomRunnable(CallbackInterface callbackInterface) 
        this.callbackInterface = callbackInterface;
    

    @Override
    public void run() 
        // Do some very long work...
        Random random = new Random();
        RandomCallbackData data = new RandomCallbackData();
        data.a = random.nextInt(100);
        data.b = random.getClass().getSimpleName();
        data.c = random.nextLong();
        callbackInterface.callback(data); // THIS REALLY NEEDS TO BE CALLED ON THE MAIN THREAD
    



interface CallbackInterface 
    void callback(RandomCallbackData data);


static class RandomCallbackData 
    public int a;
    public String b;
    public long c;

    @Override
    public String toString() 
        return "RandomCallbackData" +
                "a=" + a +
                ", b='" + b + '\'' +
                ", c=" + c +
                '';
        
    

(对不起,奇怪的格式;当我粘贴代码时堆栈溢出搞砸了)

以下是日志:

Called on Thread-3
RandomCallbackDataa=77, b='Random', c=-7871432476136355770

Process finished with exit code 0

【问题讨论】:

当你说“不工作”时,你能发布你失败的代码吗?多线程错误尤其取决于细节,因此我们需要代码来回答您的问题。 在这一位:How can I make the callback function be called on the main thread 你不能,至少没有阻塞或类似的东西(忙着等待)。当前正在执行代码的线程(您的主线程)不会突然跳到其他代码并在那里开始执行。任何系统上的线程模型都不是这样工作的。这听起来像是一个 X-Y 问题,您可能想告诉使用您实际想要完成的任务。 “正确”的做法是记录行为。让编写回调函数的人清楚,它将在与请求回调的线程不同的线程中调用。让他们明确他们有责任使用适当的同步和信号将任何信息传回他们的“主”(或任何其他)线程。 我对我的问题进行了编辑 【参考方案1】:

以下可能不是最好的答案,所以如果有更好的地方,请纠正我。这是我的代码:

Main Class:
https://pastebin.com/hSZjGWsr

Holder class for tasks:
https://pastebin.com/eme9nqtA

Callback code:
https://pastebin.com/JvXadT0L

Worker thread code:
https://pastebin.com/fZYdfn9m

这很多都可以用纯 java 来完成,但是全部手动编写会更容易理解。

以下是其工作原理的摘要:

有一个工作线程类。当您调用方法.get() 时,线程返回最终值。您也可以调用.isDone() 来确定线程是否已完成。 一个列表存储一个保持器类,它跟踪工作线程及其回调。 update 方法是在循环中调用的。 当一个线程完成时,会调用相应的回调并返回其返回值。然后线程停止,任务从列表中删除。 您可以定义任务检查竞争的频率。如果您的应用程序需要立即调用回调,请将 0 或 1 作为构造函数中的第一个参数传入。否则,请传入您希望以毫秒为单位的更新速度。 当您不再使用系统时,应调用方法markComplete

有关其工作原理的更多信息,请参阅我在代码中添加的 cmets。

我了解我的实现需要一个循环,这意味着它不是最佳选择。这是我能想到的唯一方法。再次,如果有更好的方法,请纠正我。

希望这会有所帮助! ;)

【讨论】:

以上是关于如何在 Java 中正确执行线程回调?的主要内容,如果未能解决你的问题,请参考以下文章

如何在android的jni线程中实现回调

java 子线程 回调 主线程

如何让工作线程在主 UI 线程上执行回调?

如何正确运用异步编程技术

如何正确运用异步编程技术

如何使我的 ArrayList 线程安全? Java中解决问题的另一种方法?