调用者会阻塞,直到 getFoo() 准备好值为止?

发布于 2024-09-01 06:29:01 字数 721 浏览 4 评论 0 原文

我有一个 Java Thread ,它公开了其他线程想要访问的属性:

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

问题是从运行到 foo 可用需要一些很短的时间。调用者可以在此之前调用 getFoo() 并获取 null。我宁愿他们简单地阻塞、等待,并在初始化发生后获取值。 (foo 此后永远不会更改。)准备就绪只需几毫秒,因此我对这种方法感到满意。

现在,我可以使用 wait()notifyAll() 实现这一点,并且有 95% 的机会我会做对。但我想知道你们会怎么做? java.util.concurrent 中是否有一个原语可以做到这一点,但我错过了?

或者,您将如何构建它?是的,让 foo 变得易失。是的,同步内部锁 Object 并将检查放入 while 循环中,直到它不为 null。我错过了什么吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

小苏打饼 2024-09-08 06:29:01

如果 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 也如此未声明易失性

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.

风吹雪碎 2024-09-08 06:29:01

一般来说,notify() 和notifyAll() 是您想要的方法。如果只有一个项目正在创建 Foo 并且许多线程可能会等待它,则 notification() 是危险的。但我认为这里还存在一些其他问题。

我不会让 Thread 成为存储 Foo 的地方。这样做意味着您必须在创建 Foo 之后保留一个线程。为什么不创建另一个对象来存储 Foo,并让创建线程写入它?

然后我将 getFoo() 测试 foo 并仅在它非空时等待(不要忘记将其与其自身以及与 foo setter 同步)。

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

野心澎湃 2024-09-08 06:29:01

我会使用任何 < code>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 ...
     }
     ...
   }
}

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 ...
     }
     ...
   }
}
椒妓 2024-09-08 06:29:01

尝试 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 都会期望一个 <代码>通知。您希望通知一次,然后不再需要通知,那么调用 getFoo 的任何其他线程都会阻塞,直到 foo 初始化,或者只获取 foo code> 如果已经初始化。

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.

孤单情人 2024-09-08 06:29:01

您可以使用 wait() 和 notification() 方法:

简单示例:

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

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

Simple example here:

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

So要识趣 2024-09-08 06:29:01

据我了解,并发的东西是明确创建的,不是为了立即等待并做你想做的事 - 但如果可能的话。在你的情况下,你需要等待直到有可用的东西,所以你唯一的选择是,呃,wait()。总之,看来你描述的方式是唯一正确的方式。

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.

奈何桥上唱咆哮 2024-09-08 06:29:01

如果初始化是一次性的,请尝试 java .util.concurrent.CountDownLatch

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

彻夜缠绵 2024-09-08 06:29:01

延迟初始化是一种选择吗?

synchronized Foo getFoo() {
    if (foo == null)
        foo = makeFoo();
    }
    return foo;
}

Is lazy initialization an option?

synchronized Foo getFoo() {
    if (foo == null)
        foo = makeFoo();
    }
    return foo;
}
罪歌 2024-09-08 06:29:01

也许尝试一下我奇特的 FutureValue 课程......

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();
}
}

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();
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文