从辅助线程在主线程上运行代码?

时间:2013-04-28 22:28:05

标签: java multithreading concurrency synchronization

这是一个普通的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
    });
});

提前致谢!

6 个答案:

答案 0 :(得分:26)

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

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

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();
    }
}

答案 1 :(得分:19)

如果您使用Android,使用处理程序应该可以完成这项工作吗?

new Handler(Looper.getMainLooper()).post(new Runnable () {
    @Override
    public void run () {
        ...
    }
});

答案 2 :(得分: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.
}

如果您的目的是在后台执行多项操作并检索结果,您可以设置如上所述的一些队列,或者您可以在多个期货中拆分该过程(一次开始或在需要时开始新的,甚至从另一个未来)。如果您将每个任务存储在地图或列表中,并在主线程中初始化,您可以随时检查所需的期货,并在完成后获得结果。

答案 3 :(得分:3)

您可能希望使用“甚至调度线程”&#39;大多数事件驱动的事情都会发生如果你正在使用swing:

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

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

答案 4 :(得分:0)

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

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

来自非UI线程,并且从线程返回时将从UI线程执行runnable。

答案 5 :(得分: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