Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java multithreading with Guava EventBus

I am using guava event bus. I have a server-like object that is supposed to be running all the time, listening for events to be posted to the b us. So in a junit test (MyObject is class under test), I create it in its own thread to simulate this and prevent blocking:

  @Test    
  public void test() {
      EventBus eventBus = new EventBus();

      Thread thread= new Thread() {
         @Override
         public void run()
         {
            logger.debug("Creating new thread");
            MyObject myObject = new MyObject(eventBus);
         }
      };

      thread.start();
      ...
  }

All is good, myObject is created off in its own thread, Thread1. Then later, I post an event to the event bus in my test:

eventBus.post(triggerObject);

The weird thing I am finding is that all the actions/logging from my subscribed method inside of MyObject class are executing in the main thread again. myObject waits for responses from certain other parts, and this blocks my test since it's in the main thread. Why is this happening? Is it something I am doing wrong with the EventBus or Java threading?

like image 354
yellavon Avatar asked Aug 18 '14 20:08

yellavon


Video Answer


2 Answers

Well you don't do nothing in the created thread except creating an object which finally ends up in heap (which is shared between threads), but since the reference to it is to not maintained after run, then it's also lost.

Your @Subscribe method from myObject is invoked in the same thread that calls eventBus.post(event); and not in the thread that created myObject.

The weird thing I am finding is that all the actions/logging from my subscribed method inside of MyObject class are executing in the main thread again

If your MyObject class has an @Subscribe method, then why does it need an instance of EventBus inside the constructor? You probably want

MyObject myObject = new MyObject();
eventBus.register(myObject);`

instead of MyObject myObject = new MyObject(eventBus);

like image 144
Random42 Avatar answered Sep 22 '22 00:09

Random42


It's something you are doing wrong with the EventBus: when you post an event to the EventBus, the handlers for that thread are called in the same thread that does the post.

In detail, what's happening under the covers is that EventBus is storing the queue of handlers to execute in a ThreadLocal queue. Until a thread needs the queue, it doesn't exist; when you post the first message, the ThreadLocalMap is initialized with an empty queue which is attached to the running thread. For this reason, the queue of events is drained in the same thread that posts the events.

When multiple threads share an instance of an EventBus, what's shared between them is the registry of event handlers -- the same instance(s) of the subscriber(s) will be called no matter which thread posts the event. But the subscribers get called on the same thread as the post().

See Dispatcher.PerThreadQueuedDispatcher -- that code probably doesn't match what you were using in 2014, but at the moment I think it's a lot more comprehensible than trying to find the same functionality in the original implementation.

like image 32
VoiceOfUnreason Avatar answered Sep 21 '22 00:09

VoiceOfUnreason