问题
I have a Java Thread
which exposes a property which other threads want to access:
class MyThread extends Thread {
private Foo foo;
...
Foo getFoo() {
return foo;
}
...
public void run() {
...
foo = makeTheFoo();
...
}
}
The problem is that it takes some short time from the time this runs until foo
is available. Callers may call getFoo()
before this and get a null
. I'd rather they simply block, wait, and get the value once initialization has occurred. (foo
is never changed afterwards.) It will be a matter of milliseconds until it's ready, so I'm comfortable with this approach.
Now, I can make this happen with wait()
and notifyAll()
and there's a 95% chance I'll do it right. But I'm wondering how you all would do it; is there a primitive in java.util.concurrent
that would do this, that I've missed?
Or, how would you structure it? Yes, make foo
volatile. Yes, synchronize on an internal lock Object
and put the check in a while
loop until it's not null
. Am I missing anything?
回答1:
If foo
is initialized only one time, a CountDownLatch is a great fit.
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();
}
}
Latches provide the same visibility behavior as the volatile
keyword, meaning that reading threads will see the value of foo
assigned by the thread, even though foo
isn't declared volatile
.
回答2:
In general notify() and notifyAll() are the methods you want. notify() is dangerous if only one item is creating the Foo and many threads might wait for it. But I think there are some other issues here.
I wouldn't make the Thread the place to store the Foo. Doing so means you have to keep a thread around after Foo is created. Why not make another object to store the Foo, and have the creating thread write to it?
Then I would have getFoo() test foo and only wait if it was non-null (don't forget to synchronize it with itself and with the foo setter).
回答3:
I'd use any of the BlockingQueue's in java.util.concurrent
More specifically, if there is one thread waiting for Foo and one producing it I'd use a SynchronousQueue in cases of more producers and/or more consumers my default option is a LinkedBlockingQueue, but other implementations might be better suited to your application. Your code then becomes:
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 ...
}
...
}
}
回答4:
Try the 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
...
}
}
I don't think wait
/notifyAll
will work, because every wait
will expect a notify
. You want to notify once and then never bother with notification again, then any other thread that's calling getFoo
will either block until foo
is initialized or just get foo
if it's already initialized.
回答5:
You can use the the wait() and notify() methods:
Simple example here:
http://www.java-samples.com/showtutorial.php?tutorialid=306
回答6:
As I understand, concurrent stuff is created explicitly not to wait and do what you want right away — but if it is possible at all. In your case you need to wait until something is available, so your only option is, uh, to wait()
. In short, it seems that the way you described it is the only correct way.
回答7:
If initialization is a one shot deal, try java.util.concurrent.CountDownLatch.
回答8:
Is lazy initialization an option?
synchronized Foo getFoo() {
if (foo == null)
foo = makeFoo();
}
return foo;
}
回答9:
Maybe try my fancy FutureValue class...
import java.util.concurrent.CountDownLatch;
public class FutureValue<T> {
private CountDownLatch latch = new CountDownLatch(1);
private T value;
public void set(T value) throws InterruptedException, IllegalStateException {
if (latch.getCount() == 0) {
throw new IllegalStateException("Value has been already set.");
}
latch.countDown();
this.value = value;
}
/**
* Returns the value stored in this container. Waits if value is not available.
*
* @return
* @throws InterruptedException
*/
public T get() throws InterruptedException {
latch.await();
return value;
}
}
// Usage example
class MyExampleClass {
@SuppressWarnings("unused")
private static void usageExample() throws InterruptedException {
FutureValue<String> futureValue = new FutureValue<>();
// the thread that will produce the value somewhere
new Thread(new Runnable() {
@Override
public void run() {
try {
futureValue.set("this is future");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).run();
String valueProducedSomewhereElse = futureValue.get();
}
}
来源:https://stackoverflow.com/questions/2837136/callers-block-until-getfoo-has-a-value-ready