Блок вызывающих сторон до getFoo () имеет готовое значение?

У меня есть Java Thread который выставляет свойство, к которому другие потоки хотят получить доступ:

class MyThread extends Thread {
   private Foo foo;
   ...
   Foo getFoo() {
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     ...
   }
}

Проблема состоит в том, что требуется некоторое короткое время со времени, до которого это работает foo доступно. Вызывающие стороны могут звонить getFoo() перед этим и получают a null. Я быть бы они просто блок, ожидать и получить значение, после того как инициализация произошла. (foo никогда не изменяется впоследствии.) Это будет вопрос миллисекунд, пока это не будет готово, таким образом, я доволен этим подходом.

Теперь, я могу заставить это произойти с wait() и notifyAll() и существует 95%-й шанс, я сделаю его правильно. Но я задаюсь вопросом, как Вы все сделали бы это; есть ли примитив в java.util.concurrent это сделало бы это, которое я пропустил?

Или, как Вы структурировали бы его? Да, сделайте foo энергозависимый. Да, синхронизируйтесь на внутренней блокировке Object и помещенный регистрация a while цикл, пока это не null. Я пропускаю что-нибудь?

11
задан Mohsen Nosratinia 8 May 2015 в 14:25
поделиться

8 ответов

Если foo инициализируется только один раз, то CountDownLatch отлично подойдет.

class MyThread extends Thread {

  private final CountDownLatch latch = new CountDownLatch(1);

  ...

  Foo getFoo() throws InterruptedException
  {
    latch.await(); /* Or use overload with timeout parameter. */
    return foo;
  }

  @Override
  public void run() {
    foo = makeTheFoo()
    latch.countDown();
  }

}

Защелки обеспечивают такое же поведение видимости, как и ключевое слово volatile, то есть читающие потоки будут видеть значение foo, присвоенное потоком, даже если foo не объявлено volatile.

16
ответ дан 3 December 2019 в 05:33
поделиться

Попробуйте использовать CountDownLatch :

class MyThread extends Thread {
   private volatile CountDownLatch latch;
   private Foo foo;
   MyThread(){
      latch = new CountDownLatch(1);
   }
   ...
   Foo getFoo() {
     latch.await(); // waits until foo is ready
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     latch.countDown();// signals that foo is ready
     ...
   }
}

Я не думаю, что wait / notifyAll будет работать, потому что каждое wait будет ожидать a уведомить . Вы хотите уведомить один раз и никогда больше не беспокоиться об уведомлении, тогда любой другой поток, вызывающий getFoo , либо заблокируется, пока не будет инициализирован foo , либо просто получит foo if он уже инициализирован.

1
ответ дан 3 December 2019 в 05:33
поделиться

Вы можете использовать методы wait() и notify():

Простой пример здесь:

http://www.java-samples.com/showtutorial.php?tutorialid=306

0
ответ дан 3 December 2019 в 05:33
поделиться

Если инициализация является одноразовой, попробуйте java.util.concurrent.CountDownLatch .

0
ответ дан 3 December 2019 в 05:33
поделиться

Можно ли использовать отложенную инициализацию?

synchronized Foo getFoo() {
    if (foo == null)
        foo = makeFoo();
    }
    return foo;
}
0
ответ дан 3 December 2019 в 05:33
поделиться

Как правило, вам нужны методы notify () и notifyAll (). notify () опасен, если только один элемент создает Foo, и многие потоки могут его ждать. Но я думаю, здесь есть и другие проблемы.

Я бы не стал делать Thread местом для хранения Foo. Это означает, что вы должны поддерживать поток после создания Foo. Почему бы не создать другой объект для хранения Foo и записать в него поток создания?

Тогда я бы использовал getFoo () test foo и ждать, только если он не равен нулю (не забудьте синхронизировать его с самим собой и с установщиком нижнего колонтитула).

3
ответ дан 3 December 2019 в 05:33
поделиться

Как я понимаю, concurrent создается явно не для того, чтобы ждать и делать то, что вы хотите сразу - но если это вообще возможно. В вашем случае вам нужно подождать, пока что-то будет доступно, так что ваш единственный вариант - wait(). Короче говоря, похоже, что способ, который вы описали, является единственно правильным.

0
ответ дан 3 December 2019 в 05:33
поделиться

Я бы использовал любой из BlockingQueue в java.util.concurrent Более конкретно, если есть один поток, ожидающий Foo, и один, производящий его, я бы использовал SynchronousQueue в случаях большего количества производителей и/или большего количества потребителей мой вариант по умолчанию - LinkedBlockingQueue, но другие реализации могут лучше подходить для вашего приложения. Тогда ваш код будет выглядеть следующим образом:

class MyThread extends Thread {
   private SynchronousQueue<Foo> queue = new SynchronousQueue<Foo>();
   ...
   Foo getFoo() {
     Foo foo;
     try {
        foo = queue.take();
     }
     catch (InteruptedException ex) {
        ...stuff ...
     }
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     try {
        queue.put(foo);
     }
     catch (InteruptedException ex) {
        ...stuff ...
     }
     ...
   }
}
1
ответ дан 3 December 2019 в 05:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: