Callers block until getFoo() has a value ready?
Asked Answered
M

9

11

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?

Mak answered 14/5, 2010 at 19:47 Comment(0)
C
16

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.

Cosette answered 14/5, 2010 at 20:4 Comment(0)
B
3

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).

Biographer answered 14/5, 2010 at 20:2 Comment(1)
Agree, I wish it weren't so. In this case I really have to make the object within run(). It's a GUI app with weird needs.Mak
H
1

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.

Hervey answered 14/5, 2010 at 20:12 Comment(1)
Actually, there's a subtle bug here: since latch isn't final or volatile, it's not guaranteed to be "visible" to other threads. Might not be an issue in practice, but the guarantee doesn't really cost anything.Cosette
F
1

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 ...
     }
     ...
   }
}
Favrot answered 14/5, 2010 at 20:13 Comment(2)
SynchronousQueue is a handy construct in many cases, but it doesn't fit the requirement here very well: foo is only initialized once, and read possibly many times. This would only allow foo to be read once. Other attempts to read the property would block forever.Cosette
Yes, you are correct, I had missed the "only initialized once" part. I thought this was more of a producer/consumer situation.Favrot
E
0

You can use the the wait() and notify() methods:

Simple example here:

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

Evildoer answered 14/5, 2010 at 19:53 Comment(2)
Definitely, I can roll a solution by hand. I think it's a tad more complex than this -- the field should be volatile and we need notifyAll() to start -- so I was hoping for comments on what else I need, or a pre-packaged solution that's definitely right.Mak
@Sean In that case I would go with DJClayworth's suggestionEvildoer
S
0

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.

Schlep answered 14/5, 2010 at 19:55 Comment(0)
F
0

If initialization is a one shot deal, try java.util.concurrent.CountDownLatch.

Flower answered 14/5, 2010 at 20:5 Comment(0)
W
0

Is lazy initialization an option?

synchronized Foo getFoo() {
    if (foo == null)
        foo = makeFoo();
    }
    return foo;
}
Walkerwalkietalkie answered 14/5, 2010 at 20:6 Comment(1)
Not in this case since the foo must be made in the other thread.Mak
B
0

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();
}
}
Buhler answered 20/9, 2017 at 2:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.