Running code on the main thread from a secondary thread?
Asked Answered
F

6

35

This is a general Java question and not an Android one first off!

I'd like to know how to run code on the main thread, from the context of a secondary thread. For example:

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

That sort of thing - I realise my example is a little poor since in Java you don't need to be in the main thread to print something out, and that Swing has an event queue also - but the generic situation where you might need to run say a Runnable on the main thread while in the context of a background thread.

EDIT: For comparison - here's how I'd do it in 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
    });
});

Thanks in advance!

Franciscofranciska answered 28/4, 2013 at 22:28 Comment(0)
E
33

There is no universal way to just send some code to another running thread and say "Hey, you, do this." You would need to put the main thread into a state where it has a mechanism for receiving work and is waiting for work to do.

Here's a simple example of setting up the main thread to wait to receive work from other threads and run it as it arrives. Obviously you would want to add a way to actually end the program and so forth...!

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();
    }
}
Erhart answered 28/4, 2013 at 22:38 Comment(7)
Ah okay, so in practice I guess this sort of defeats the purpose of using a background thread since the main thread is blocked waiting for the background thread. That said, it does look like a pretty neat mechanism! I'll read up more about queues now!Franciscofranciska
You could certainly have the main thread doing some other work and just check on the queue at intervals rather than do nothing but block on it. But at some point it stops being worth home-brewing things Swing provides perfectly good tools for :)Erhart
Actually, that's quite a good idea for what I'm doing - I'm not using Swing as the project is actually a simple game written using the Slick2d engine (otherwise you're right; I would just use Swing's built-in tools for it!)Franciscofranciska
What an ugly solution you use. iOS way is so pretty: just get, retrieve the main thread and then run code on it.Gompers
What do you think the actual implementation of the "pretty" iOS APIs looks like under the hood? :) There are pretty APIs for java/android too, but the original question specifically asked how it actually works without using the library tools.Erhart
@János we have looper and handler in Android that you can post a runnable to the ui thread using them. this is pure java and what the looper and handler is doing under the hood.Cailean
@Franciscofranciska You can use Swing's threading tools even if you aren't using Swing as a part of your project.Abbe
A
30

In case you are on Android, using a Handler should do the job?

new Handler(Looper.getMainLooper()).post(new Runnable () {
    @Override
    public void run () {
        ...
    }
});
Algin answered 20/5, 2013 at 7:25 Comment(4)
I know there is a method called runOnUIThread() but not in every circumstance the code is written in Activity. This answer saved me a lot of time. Thanks!Millford
the Q is not about android , it doesnt even have the Android Tag.Cailean
Yes but he has extended the previous answer. This allows others with the same question, like me, to be given a clear description about different platforms running javaConroy
It is questioned for JavaAuxiliaries
A
7

An old discussion, but if it is a matter of sending request to the main thread (an not the opposite direction) you can also do it with futures. The basic aim is to execute something in background and, when it is finished, to get the result:

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.
}

If your intention is to do several stuff in background and retrieve the results, you can set some queues as said above or you can split the process in several futures (starting all at once or starting a new one when needed, even from another future). If you store each task in a map or a list, initialized in the main thread, you can check the futures that you want at anytime and get their results when they are done.

Alessi answered 15/1, 2015 at 1:11 Comment(0)
M
4

You may want to use the 'even dispatching thread' where most event driven things happen. If you are using swing then:

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

Or create a class that implements Runnable and pass it into invokeLater().

Moorer answered 6/9, 2015 at 8:27 Comment(0)
B
3

If you're using JavaFX, which I highly recommend, then you can use

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

from within your non-UI thread, and the runnable will executed from the UI thread on return from your thread.

Bowerman answered 16/4, 2018 at 16:49 Comment(0)
A
0

A little late to the party but I think that my approach is a little bit different.

Modifying Affe's solution a little bit

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

output

initial current thread background thread
main
Anna answered 7/8, 2021 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.