У меня есть 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
. Я пропускаю что-нибудь?
Если 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
.
Попробуйте использовать 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 он уже инициализирован.
Вы можете использовать методы wait() и notify():
Простой пример здесь:
Если инициализация является одноразовой, попробуйте java.util.concurrent.CountDownLatch .
Можно ли использовать отложенную инициализацию?
synchronized Foo getFoo() {
if (foo == null)
foo = makeFoo();
}
return foo;
}
Как правило, вам нужны методы notify () и notifyAll (). notify () опасен, если только один элемент создает Foo, и многие потоки могут его ждать. Но я думаю, здесь есть и другие проблемы.
Я бы не стал делать Thread местом для хранения Foo. Это означает, что вы должны поддерживать поток после создания Foo. Почему бы не создать другой объект для хранения Foo и записать в него поток создания?
Тогда я бы использовал getFoo () test foo и ждать, только если он не равен нулю (не забудьте синхронизировать его с самим собой и с установщиком нижнего колонтитула).
Как я понимаю, concurrent создается явно не для того, чтобы ждать и делать то, что вы хотите сразу - но если это вообще возможно. В вашем случае вам нужно подождать, пока что-то будет доступно, так что ваш единственный вариант - wait()
. Короче говоря, похоже, что способ, который вы описали, является единственно правильным.
Я бы использовал любой из 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 ...
}
...
}
}