Running Code in Main Thread from Another Thread

Running code in main thread from another thread

NOTE: This answer has gotten so much attention, that I need to update it. Since the original answer was posted, the comment from @dzeikei has gotten almost as much attention as the original answer. So here are 2 possible solutions:

1. If your background thread has a reference to a Context object:

Make sure that your background worker threads have access to a Context object (can be the Application context or the Service context). Then just do this in the background worker thread:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
@Override
public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. If your background thread does not have (or need) a Context object

(suggested by @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
@Override
public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

Running code on the main thread from a secondary thread?

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

Move to main thread after another thread go to sleep

What you have done in your code above created a thread that runs concurrently with the main thread. What actually happens is:

  1. Main thread starts and initiates AutoUpdater thread
  2. The two threads will run concurrently. In fact, the Main thread may even terminate before the AutoUpdater thread has really started.
  3. The auto-update thread processes the clients ONCE, then sleeps for 24 hours and then terminates and your program completely terminates at this point.

So sticking with what you have, the first step is to get the AutoUpdater thread to run every 24 hours. One way you could do this is to keep the thread running and put a while loop in the run method so that it doesn't terminate but processes the clients collection every 24 hours. So now AutoUpdater might look like this:

public class AutoUpdater implements Runnable {
public void run() {
while (true) {
try {
System.out.println("Thread is running...");
for (int i = 0; i < clients.size(); i++) {
// do something...
}
} finally {
System.out.println("Thread ended.\n");
}

int time = 1000 * 60 * 60 * 24;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
System.out.println("Something interrputed thread while running.");
}
}
}
}

However, the code above has some issues in that it will drift. If for example, processing takes an hour then the next time it runs will be 25 hours after the last run initial started. Fortunately, Java provides a thread executor service that will run your thread on a fixed schedule called ScheduledExecutorService. So let's unwind the while loop and introduce the executor instead.

public class AutoUpdater implements Runnable {
public void run() {
System.out.println("Thread is running...");
for (int i = 0; i < clients.size(); i++) {
// do something...
}
System.out.println("Thread ended.\n");
}
}

public static class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(
new AutoUpdater(clients, lock.writeLock()),
0,
24,
TimeUnit.HOURS);

System.out.println("This is a test");
}
}

Now we've got the auto-updater thread running every 24 hours from when we started the process. If you want to fix the time, i.e. at 8 AM every day you can either calculate the delay till that time (though this won't take into account daylight saving issues) or use a third-party library like Quartz to schedule at a specific time of day.

I want to be able to run the thread first and then move to any other thread, for example, my main.

Presumably by this, you mean that you want to stop other threads from executing while the Auto-Update is running. For this, you have several options available. In the first instance, you can use a monitor to synchronize and lock threads, i.e.

Object sharedMonitor = new byte[0]

// In your auto-updater and other threads
synchronised(sharedMonitor ) {

}

The syntax above will only allow a single thread to enter a synchronized block at a time for the same monitor instance. This would work fine in the example above where you only have the two threads. If you have more than the two threads it becomes problematic as you only really want the other threads to block when the auto-updater is running. In this case, this isn't the right solution for you. What you are after is something that will let all the threads run concurrently until the auto-updater needs to run and then they all need to block and wait for the auto-updater to finish. Fortunately for you, Java has a ReadWriteLock which does exactly that.

So let's add that lock and use it.

public static class Main {
private static List<String> clients = new ArrayList<>();

public static void main(String[] args) throws IOException, ClassNotFoundException {
ReadWriteLock lock = new ReentrantReadWriteLock();

ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(
new AutoUpdater(clients, lock.writeLock()),
0,
24,
TimeUnit.HOURS);

Lock readLock = lock.readLock();
while (true) {
try {
readLock.lock();
System.out.println("This is a test");
} finally {
readLock.unlock();
}
}
}
}

So above we have:

  1. Added a read-write lock
  2. Passed the write lock to AutoUpdater
  3. Added a while loop to the main method so it doesn't terminate and can do whatever run-of-the-mill processing it is meant to be doing.
  4. In the while loop we've acquired the read lock at the start and released it at the end.

The last piece of the puzzle is to use the write lock in AutoUpdater

public class AutoUpdater implements Runnable {
public void run() {
try {
lock.lock();
System.out.println("Thread is running...");
// do something...
}
} finally {
System.out.println("Thread ended.\n");
lock.unlock();
}
}
}

Call function in main thread from another thread?

A Queue would actually be the appropriate method for going about this. You have each updated value added the the queue and the main thread will wait for an item to be added each time. You could format it as some kind of "Event Stream" so you know where it comes from. Each value in the queue might look something like:

{
"source": "THREAD_A",
"value": 42
}

I'm assuming in each thread you have some kind of produce() function. It might look something like this:

def produce(event_queue):
while True:
value = do_something()
event_queue.put({"source": ME, "value": value})

And then in your main thread:

values = {"THREAD_A": 0, "THREAD_B": 0, "THREAD_C": 0}
while True:
s = sum(values.values())
# ...
last_event = event_queue.get()
values[last_event["source"]] = last_event["value"]

The get() function causes the main thread to wait until the next event comes in, then it continues and processes that value. As such, it'll recalculate exactly once for each new message that arrives.

To keep with your on_message() metaphor, you can just think of last_event as the value you call on_message() with, and everything that happens immediately after it as the interior of that method. I mean, you could write it as it's own method if you wanted.

values = {"THREAD_A": 0, "THREAD_B": 0, "THREAD_C": 0}
def on_message(v, message):
v[last_event["source"]] = last_event["value"]
s = sum(v.values())
# ...

while True:
last_message = event_queue.get()
on_message(values, last_message)

Hope this gives you a bit of an idea.

Run method on UI thread from another thread

The SynchronizationContext class was meant to solve this problem. Copy the value of its Current property in the constructor, use its Post() or Send() method later. This ensures your library will work with any GUI class library. Like this:

class MediaPlayer {
public MediaPlayer() {
callersCtx = System.Threading.SynchronizationContext.Current;
//...
}

private void FireOnTrackComplete() {
if (callersCtx == null) FireOnTrackCompleteImpl();
else callersCtx.Post(new System.Threading.SendOrPostCallback((_) => FireOnTrackCompleteImpl()), null);
}

protected virtual void FireOnTrackCompleteImpl() {
var handler = OnTrackComplete;
if (handler != null) handler(this, loadedTrack);
}

private System.Threading.SynchronizationContext callersCtx;
}


Related Topics



Leave a reply



Submit