Многопоточность Java с помощью Guava EventBus

Я использую шину событий guava. У меня есть объект, похожий на сервер, который должен работать все время, прослушивая события, которые будут отправлены в b us. Итак, в тесте junit (MyObject — тестируемый класс) я создаю его в собственном потоке, чтобы имитировать это и предотвратить блокировку:

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

Все хорошо, myObject создается в собственном потоке Thread1. Затем позже я отправляю событие в шину событий в своем тесте:

eventBus.post(triggerObject);

Странная вещь, которую я обнаружил, заключается в том, что все действия/регистрация из моего подписанного метода внутри класса MyObject снова выполняются в основном потоке. myObject ожидает ответов от некоторых других частей, и это блокирует мой тест, поскольку он находится в основном потоке. Почему это происходит? Это что-то, что я делаю неправильно с потоками EventBus или Java?


person yellavon    schedule 18.08.2014    source источник


Ответы (2)


Ну, вы ничего не делаете в созданном потоке, кроме создания объекта, который в конечном итоге оказывается в куче (которая разделяется между потоками), но поскольку ссылка на него не сохраняется после запуска, она также теряется.

Ваш метод @Subscribe из myObject вызывается в том же потоке, который вызывает eventBus.post(event);, а не в потоке, создавшем myObject.

Странная вещь, которую я обнаружил, заключается в том, что все действия/регистрация из моего подписанного метода внутри класса MyObject снова выполняются в основном потоке.

Если в вашем классе MyObject есть метод @Subscribe, то зачем ему экземпляр EventBus внутри конструктора? Вы, вероятно, хотите

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

вместо MyObject myObject = new MyObject(eventBus);

person Random42    schedule 18.08.2014
comment
Я регистрируюсь внутри класса MyObject. Итак, в конструкторе я делаю что-то вроде: eventBus.register(this);. Что-то не так с этим? - person yellavon; 19.08.2014
comment
Это «запах кода», передающий this другому объекту внутри конструктора. Технически вы отдаете свой объект (myObject) другому объекту (eventBus), пока ваш исходный объект не полностью инициализирован. Дополнительные сведения см. здесь: ibm.com/developerworks/ java/библиотека/j-jtp0618/index.html#2 - person Random42; 19.08.2014

Это что-то, что вы делаете неправильно с EventBus: когда вы отправляете событие в EventBus, обработчики для этого потока вызываются в том же потоке, который делает публикацию.

В частности, под обложкой происходит то, что EventBus хранит очередь обработчиков для выполнения в очереди ThreadLocal. Пока потоку не нужна очередь, она не существует; когда вы отправляете первое сообщение, ThreadLocalMap инициализируется пустой очередью, которая присоединена к работающему потоку. По этой причине очередь событий опустошается в том же потоке, который публикует события.

Когда несколько потоков совместно используют экземпляр EventBus, то, что является общим для них, — это реестр обработчиков событий — одни и те же экземпляры подписчика (ов) будут вызываться независимо от того, какой поток публикует событие. Но подписчики вызываются в том же потоке, что и post().

См. Dispatcher.PerThreadQueuedDispatcher -- этот код, вероятно, не соответствует тому, что вы использовали в 2014 году, но на данный момент я думаю, что он намного понятнее, чем попытки найти ту же функциональность в оригинальной реализации.

person VoiceOfUnreason    schedule 17.08.2015