30

这是一个一般的 Java 问题,而不是 Android 问题!

我想知道如何从辅助线程的上下文中在主线程上运行代码。例如:

new Thread(new Runnable() {
        public void run() {
            //work out pi to 1,000 DP (takes a while!)

            //print the result on the main thread
        }
    }).start();

那种事情——我意识到我的例子有点糟糕,因为在 Java 中你不需要在主线程中打印一些东西,而且 Swing 也有一个事件队列——但是你可能需要的一般情况在后台线程的上下文中运行主线程上的 Runnable。

编辑:为了比较 - 这是我在 Objective-C 中的做法:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0UL), ^{
    //do background thread stuff

    dispatch_async(dispatch_get_main_queue(), ^{
        //update UI
    });
});

提前致谢!

4

6 回答 6

30

没有通用的方法可以将一些代码发送到另一个正在运行的线程并说“嘿,你,做这个”。您需要将主线程置于具有接收工作机制并等待工作完成的状态。

这是一个设置主线程以等待从其他线程接收工作并在它到达时运行它的简单示例。显然你会想要添加一种方法来实际结束程序等等......!

public static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();

public static void main(String[] args) throws Exception {
    new Thread(new Runnable(){
        @Override
        public void run() {
            final int result;
            result = 2+3;
            queue.add(new Runnable(){
                @Override
                public void run() {
                    System.out.println(result);
                }
            });
        }
    }).start();

    while(true) {
        queue.take().run();
    }
}
于 2013-04-28T22:38:26.440 回答
24

如果您在 Android 上,使用 Handler 应该可以完成这项工作吗?

new Handler(Looper.getMainLooper()).post(new Runnable () {
    @Override
    public void run () {
        ...
    }
});
于 2013-05-20T07:25:04.573 回答
7

一个古老的讨论,但如果是向主线程发送请求(不是相反的方向),你也可以用期货来做。基本目的是在后台执行某些操作,并在完成后获得结果:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    // create the task to execute
    System.out.println("Main: Run thread");
    FutureTask<Integer> task = new FutureTask<Integer>(
            new Callable<Integer>() {

                @Override
                public Integer call() throws Exception {
                    // indicate the beginning of the thread
                    System.out.println("Thread: Start");

                    // decide a timeout between 1 and 5s
                    int timeout = 1000 + new Random().nextInt(4000);

                    // wait the timeout
                    Thread.sleep(timeout);

                    // indicate the end of the thread
                    System.out.println("Thread: Stop after " + timeout + "ms");

                    // return the result of the background execution
                    return timeout;
                }
            });
    new Thread(task).start();
    // here the thread is running in background

    // during this time we do something else
    System.out.println("Main: Start to work on other things...");
    Thread.sleep(2000);
    System.out.println("Main: I have done plenty of stuff, but now I need the result of my function!");

    // wait for the thread to finish if necessary and retrieve the result.
    Integer result = task.get();

    // now we can go ahead and use the result
    System.out.println("Main: Thread has returned " + result);
    // you can also check task.isDone() before to call task.get() to know
    // if it is finished and do somethings else if it is not the case.
}

如果您打算在后台做几件事并检索结果,您可以如上所述设置一些队列,或者您可以将进程拆分为多个未来(一次开始或在需要时开始一个新的,甚至从另一个未来开始) . 如果您将每个任务存储在地图或列表中,并在主线程中初始化,您可以随时检查您想要的期货,并在完成后获取它们的结果。

于 2015-01-15T01:11:11.793 回答
4

您可能希望使用大多数事件驱动的事情发生的“均匀调度线程”。如果您使用的是摇摆,那么:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Your code here.
        }
    });

或者创建一个实现 Runnable 的类并将其传递给 invokeLater()。

于 2015-09-06T08:27:49.197 回答
1

如果您使用的是我强烈推荐的 JavaFX,那么您可以使用

    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            alert(text);
        }
    });

从您的非 UI 线程中,并且可运行将在从您的线程返回时从 UI 线程执行。

于 2018-04-16T16:49:53.320 回答
0

聚会有点晚了,但我认为我的方法有点不同。

稍微修改一下 Affe 的解决方案

 public static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

  public static void main(String[] args) {

    Thread myThread = new Thread(

        () -> {
          String name = Thread.currentThread().getName();
          System.out.println("initial current thread " + name);

          queue.add(() -> System.out.println(Thread.currentThread().getName()));
        });

    myThread.setName("background thread");
    myThread.start();

    try {
      myThread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    while (!queue.isEmpty()) {
      try {
        queue.take().run();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

输出

initial current thread background thread
main
于 2021-08-07T09:15:04.983 回答