2

I am facing some problems trying to call main thread in my background thread.

Based on this post: Running code in main thread from another thread

The solution should be:

private void runOnMainThread() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {

        @Override
        public void run() {
            // Do something
            ottoBus.post(new MyObjectEvent(mMyObject));

            // End do something
            mMyObject = null;
        }
    });
}

However my background thread is still being able to read Bluetooth socket data between "Do something" and "End do something"

What am I missing here? Is it possible to lock the background thread, while "Do something" is being executed?

My code for reading socket data is following:

InputStream stream = null;
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
String data = "";

try {
    stream = mSocket.getInputStream();

    byte[] bytes = new byte[20];

    int numRead = 0;
    while ((numRead = stream.read(bytes)) >= 0) {
        String s = new String(bytes, 0, numRead);
        if (mMyObject != null) {
            fillData(s); // Can cause NPE
        } else {
            mMyObject = new MyObject();
            fillData(s);
        }

        // This should be synchronised call
        runOnMainThread();

Thanks.

Community
  • 1
  • 1
Niko
  • 8,093
  • 5
  • 49
  • 85
  • That two different `Threads` run parallel shouldn't be surprising. There are a few things you could do, but you can never make the core issue - that there are two different `Threads` - disappear. – Xaver Kapeller Jul 16 '14 at 12:58
  • But is there some method for blocking the background thread while main thread code is executed? – Niko Jul 16 '14 at 13:08
  • Theoretically, but that shouldn't be necessary. I guess the other `Thread` is reading data from some input? Buffer the read data, and pass it bit after bit to the other `Thread`. There shouldn't be any concurrency issues if done right. – Xaver Kapeller Jul 16 '14 at 13:14
  • And if you don't want to read data while processing it, then why don't you do both in the same `Thread`? – Xaver Kapeller Jul 16 '14 at 13:14
  • The "do something code" needs to be run in main thread and while running it, the background thread should not read new data. – Niko Jul 16 '14 at 13:16
  • Can you tell me more about your situation? Why shouldn't it read data while the "do something code" is running? And in that case can't you just stop the other `Thread` and restart it when you are done? – Xaver Kapeller Jul 16 '14 at 13:20
  • Basically the background thread fills an object with the data, in main thread the object is passed to UI and then set to null, after that the background thread should create a new object and start filling it, but now the object is set to null meanwhile background thread fills it and I get NPE. – Niko Jul 16 '14 at 13:28
  • Yes I suspected something like this, you should never just share `Objects` between `Threads` like this. Doing something like this is a common source of concurrency issues as you have experienced first hand. I can help you fix it if you can edit more code into your question especially the code where you pass the `Object` between the `Threads`. – Xaver Kapeller Jul 16 '14 at 13:30

3 Answers3

2

You will need to use a Java pattern called wait/notify. Simply put: it defines two threads, a producer and a consumer, so that the consumer, after initiating the producer, stops and waits until the producer thread has completed.

It goes like this:

static final object uiActionMonitor = new Object();
transient boolean uiCompleted;

void network_thread_run() {

    int numRead = 0;
    while ((numRead = stream.read(bytes)) >= 0) {
        String s = new String(bytes, 0, numRead);

    // This should be synchronised call
    uiCompleted = false;
    runOnMainThread();

    synchronized(uiActionMonitor) {  //<---------- wait for UI to complete
       while (!uiCompleted) {
            uiActionMonitor.wait();
       }
    }

}

And the UI code:

private void runOnMainThread() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {

        @Override
        public void run() {
            // Do something

            // End do something
            uiCompleted = true;
            synchronized(uiActionMonitor) {  //<---------- release networking thread
                uiActionMonitor.notifyAll();                 
            }
        }
    });
}

Copy the synchronization logic exactly as is. This is where many developers get it wrong.

I must admit I fail to see why you need to block your networking thread while the UI thread is handling your message...

Gilad Haimov
  • 5,767
  • 2
  • 26
  • 30
  • It's because in main thread I pass event with Otto and the object is shared between threads because it holds massive amount of data so I do not want to make a copy of it. – Niko Jul 16 '14 at 13:58
1

I find CountDownLatch to be the simplest way to accomplish this sort of thing. Here's a reusable method for running Runnables on the main thread and blocking on their completion:

private static final Handler mainHandler = new Handler(Looper.getMainLooper());

private static void runOnMainThreadBlocking(Runnable runnable) throws InterruptedException {
  CountDownLatch completionSignal = new CountDownLatch(1);

  mainHandler.post(new Runnable() {
    @Override public void run() {
      runnable.run();
      completionSignal.countDown();
    }
  });

  completionSignal.await();
}
Daniel Lubarov
  • 7,796
  • 1
  • 37
  • 56
0

I think you need to use locks or synchronized blocs. You can take a look into the java concurency documentation and more specificaly this and this part. This way you can guaranty that on portion of the code won't be executed muliple times in parallel.

user2641570
  • 804
  • 8
  • 20