Java: шаблон наблюдателя в новом потоке - PullRequest
4 голосов
/ 17 декабря 2011

У меня следующая проблема. Имеется интерфейс EventNotifier для шаблона наблюдателя:

public interface EventNotifier {
    void newEvent(final String value);
}

Класс, который реализует этот Интерфейс, может зарегистрироваться в другом классе, который очень часто вызывает метод newEvent. Интерфейс предоставляется внешней библиотекой, поэтому я не могу его изменить. До сих пор я реализовал это с помощью анонимного класса:

Thread t = new Thread(new Runnable() {

    @Override    
    public void run() {

      watcher = new Watcher(new EventNotifier() {

          @Override
          public void newEvent(String value) {
              //do some stuff
              //will be called more than 20 times per second
          }
       });
 });
 t.start();

Для лучшей читабельности кода я хотел бы представить этот анонимный класс в новый класс, который расширяет Thread (потому что обработка должна быть параллельна другим вещам).

Как я могу написать поток, который ничего не делает (без бесконечного цикла и т. Д.), Но ожидает вызова метода newEvent? Проблема в том, что newEvent будет вызываться более 20 раз в секунду, поэтому я не могу запустить новый поток для каждого вызова, но все это должно быть в потоке.

Надеюсь, у вас проблема, и кто-нибудь может мне помочь.

Ответы [ 3 ]

6 голосов
/ 17 декабря 2011

Что делает ваше сообщение непонятным, так это то, что EventNotifier на самом деле является наблюдателем / слушателем (он получает событие, но не запускает его), а Watcher на самом деле уведомитель (это наблюдательсоздает событие и вызывает метод newEvent).

Теперь я буду использовать термины наблюдаемый и наблюдатель .Наблюдаемое запускает события и, таким образом, вызывает метод newEvent наблюдателя.

Если вы хотите, чтобы обработка события выполнялась в отдельном потоке, используйте BlockingQueue.Запустите поток, который зацикливается бесконечно и пытается take() из очереди на каждой итерации.Зарегистрируйте наблюдателя в наблюдаемой, которая просто берет полученное событие и put() оно в очереди блокировки.

2 голосов
/ 17 декабря 2011

Вы можете использовать Executor , чтобы избежать кодирования BlockingQueue и опроса Thread вручную.

В вашем основном классе у вас будет что-то вроде:

Executor eventExecutor = Executors.newSingleThreadExecutor();
// ...
watcher = new Watcher(new EventNotifier() {
    public void newEvent(final String value) {
        eventExecutor.execute(new ConcurrentEventHandler(value));
    }   
}); 

И обработчик одновременных событий, который выполняет обработку в фоновом потоке:

class ConcurrentEventHandler implements EventNotifier, Runnable {
        private final String value;

        public ConcurrentEventHandler(String value) {
            this.value = value;
        }

        public void newEvent(final String value) {
            // do some stuff
        }

        public void run() {
            // executed in background thread
            newEvent(value);
        }
    }

Я реализовал EventNotifier здесь, но это, конечно, не нужно

1 голос
/ 17 декабря 2011

Использовать старую wait/notifyAll:

// we need final object to synchronize your code and library code on it
// it's convenient to make this object hold all needed data to be passed from library as well
// in your case AtomicBoolean should suffice (we can't use simple `final Boolean`, since it would be impossible to assign new value to it, as we need in code below).

final AtomicBoolean called = new AtomicBoolean(false);

EventNotifier en = new EventNotifier() {
          @Override
          public void newEvent(String value) {
               // this will be called by your external library
               synchronized(called) {
                    called.set(true); called.notifyAll();
               }
          }
       };

Thread t = new Thread(new Runnable() {
    @Override    
    public void run() {
         synchronized(called) {
            // wait here until library call occurs
            while (!called.get()) {
                 try {
                     called.wait();
                 } catch (InterruptedException e) {
                   // handle exception as desired
                 }
            }
            // reset called flag asap, so we will know when next call occurs
            called.set(false);
            ... // do your stuff
         }
    );
 });
t.start();

Для общего ознакомления с многопоточным программированием на Java прочитайте учебник . Затем, если вам интересна тема, прочитайте «Параллелизм Java на практике» Гетца.

В случае, если вам нужно обработать value, переданный в newEvent из библиотеки, вам понадобится какой-то BlockingQueue вместо простого Boolean.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...