подобные вопросы:
У меня есть объект с методом, который я хотел бы выставить клиентам библиотеки (особенно пишущий сценарий клиентов) как что-то как:
interface MyNiceInterface
{
public Baz doSomethingAndBlock(Foo fooArg, Bar barArg);
public Future doSomething(Foo fooArg, Bar barArg);
// doSomethingAndBlock is the straightforward way;
// doSomething has more control but deals with
// a Future and that might be too much hassle for
// scripting clients
}
но примитивный "материал", который я имею в наличии, является рядом событийно-ориентированных классов:
interface BazComputationSink
{
public void onBazResult(Baz result);
}
class ImplementingThing
{
public void doSomethingAsync(Foo fooArg, Bar barArg, BazComputationSink sink);
}
то, где ImplementingThing берет исходные данные, делает некоторый тайный материал как вещи постановки в очередь на очереди задачи, и затем позже, когда результат происходит, sink.onBazResult()
обращен поток, который может или не может быть тем же потоком, как ImplementingThing.doSomethingAsync () назвали.
Существует ли способ, которым я могу использовать событийно-ориентированные функции, которые я имею, наряду с примитивами параллелизма, для реализации MyNiceInterface, настолько пишущие сценарий клиенты могут счастливо ожидать на блокирующемся потоке?
править: я могу использовать FutureTask для этого?
Используя свою собственную будущую реализацию:
public class BazComputationFuture implements Future<Baz>, BazComputationSink {
private volatile Baz result = null;
private volatile boolean cancelled = false;
private final CountDownLatch countDownLatch;
public BazComputationFuture() {
countDownLatch = new CountDownLatch(1);
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
if (isDone()) {
return false;
} else {
countDownLatch.countDown();
cancelled = true;
return !isDone();
}
}
@Override
public Baz get() throws InterruptedException, ExecutionException {
countDownLatch.await();
return result;
}
@Override
public Baz get(final long timeout, final TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
countDownLatch.await(timeout, unit);
return result;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public boolean isDone() {
return countDownLatch.getCount() == 0;
}
public void onBazResult(final Baz result) {
this.result = result;
countDownLatch.countDown();
}
}
public Future<Baz> doSomething(Foo fooArg, Bar barArg) {
BazComputationFuture future = new BazComputationFuture();
doSomethingAsync(fooArg, barArg, future);
return future;
}
public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {
return doSomething(fooArg, barArg).get();
}
Решение создает внутренний обратный отсчет CountDownLatch, который очищается после получения обратного вызова. Если пользовательские вызовы поступают, то CountDownLatch используется для блокировки вызывающего потока до завершения вычислений и вызова обратного вызова onBazResult. Функция CountDownLatch гарантирует, что если вызов произойдет до того, как будет вызвана функция get(), то метод get() сразу же вернется с результатом.
Ну, есть простое решение:
public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {
final AtomicReference<Baz> notifier = new AtomicReference();
doSomethingAsync(fooArg, barArg, new BazComputationSink() {
public void onBazResult(Baz result) {
synchronized (notifier) {
notifier.set(result);
notifier.notify();
}
}
});
synchronized (notifier) {
while (notifier.get() == null)
notifier.wait();
}
return notifier.get();
}
Конечно, это предполагает, что ваш Baz
результат никогда не будет нулевым...