sincrono sincronas programacion funciones ejecucion controlar codigo asincrono asincronia asincrona java concurrency asynchronous blocking

java - sincronas - Envolviendo un cálculo asincrónico en un cálculo síncrono(de bloqueo)



funciones sincronas javascript (6)

Un ejemplo muy simple, solo para entender CountDownLatch sin ningún código adicional.

Un java.util.concurrent.CountDownLatch es una construcción de simultaneidad que permite que uno o más subprocesos esperen a que se complete un determinado conjunto de operaciones.

Un CountDownLatch se inicializa con un conteo dado. Este conteo se reduce mediante llamadas al método countDown() . Los hilos que esperan que esta cuenta llegue a cero pueden llamar a uno de los métodos await() . La llamada a await() bloquea el hilo hasta que el conteo llega a cero.

A continuación se muestra un ejemplo simple. Después de que el Decrementador ha llamado a countDown() 3 veces en CountDownLatch , el Waiter en espera se libera de la llamada countDown() .

También puede mencionar algún TimeOut para esperar.

CountDownLatch latch = new CountDownLatch(3); Waiter waiter = new Waiter(latch); Decrementer decrementer = new Decrementer(latch); new Thread(waiter) .start(); new Thread(decrementer).start(); Thread.sleep(4000); public class Waiter implements Runnable{ CountDownLatch latch = null; public Waiter(CountDownLatch latch) { this.latch = latch; } public void run() { try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Waiter Released"); } }

// --------------

public class Decrementer implements Runnable { CountDownLatch latch = null; public Decrementer(CountDownLatch latch) { this.latch = latch; } public void run() { try { Thread.sleep(1000); this.latch.countDown(); Thread.sleep(1000); this.latch.countDown(); Thread.sleep(1000); this.latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }

Reference

Si no desea utilizar un CountDownLatch o su requisito es algo similar a la funcionalidad de Facebook y a diferencia. Significa que si se está llamando a un método, no llame al otro método.

En ese caso, puede declarar un

private volatile Boolean isInprocessOfLikeOrUnLike = false;

y luego puede verificar al comienzo de su llamada de método que si es false entonces el método de llamada de lo contrario retorna ... depende de su implementación.

preguntas similares:

Tengo un objeto con un método que me gustaría exponer a los clientes de la biblioteca (especialmente a los clientes de scripting) como algo como:

interface MyNiceInterface { public Baz doSomethingAndBlock(Foo fooArg, Bar barArg); public Future<Baz> 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 }

pero el "material" primitivo que tengo disponible es un conjunto de clases dirigidas por eventos:

interface BazComputationSink { public void onBazResult(Baz result); } class ImplementingThing { public void doSomethingAsync(Foo fooArg, Bar barArg, BazComputationSink sink); }

donde ImplementingThing toma entradas, hace algunas cosas arcanas como enqueueing cosas en una cola de tareas, y luego, cuando ocurre un resultado, se llama a sink.onBazResult() en un hilo que puede ser o no el mismo hilo que ImplementingThing.doSomethingAsync () fue llamado.

¿Existe alguna forma de que pueda usar las funciones controladas por eventos que tengo, junto con las primitivas de concurrencia, para implementar MyNiceInterface, de modo que los clientes de scripting puedan esperar con gusto un hilo de bloqueo?

editar: ¿puedo usar FutureTask para esto?


Aquí hay una solución más genérica basada en la respuesta de Paul Wagland:

public abstract class AsyncRunnable<T> { protected abstract void run(AtomicReference<T> notifier); protected final void finish(AtomicReference<T> notifier, T result) { synchronized (notifier) { notifier.set(result); notifier.notify(); } } public static <T> T wait(AsyncRunnable<T> runnable) { final AtomicReference<T> notifier = new AtomicReference<>(); // run the asynchronous code runnable.run(notifier); // wait for the asynchronous code to finish synchronized (notifier) { while (notifier.get() == null) { try { notifier.wait(); } catch (InterruptedException ignore) {} } } // return the result of the asynchronous code return notifier.get(); } }

Aquí hay un ejemplo de cómo usarlo ::

String result = AsyncRunnable.wait(new AsyncRunnable<String>() { @Override public void run(final AtomicReference<String> notifier) { // here goes your async code, e.g.: new Thread(new Runnable() { @Override public void run() { finish(notifier, "This was a asynchronous call!"); } }).start(); } });

Una versión más detallada del código se puede encontrar aquí: http://pastebin.com/hKHJUBqE

EDITAR: El ejemplo relacionado con la pregunta sería:

public Baz doSomethingAndBlock(final Foo fooArg, final Bar barArg) { return AsyncRunnable.wait(new AsyncRunnable<Baz>() { @Override protected void run(final AtomicReference<Baz> notifier) { doSomethingAsync(fooArg, barArg, new BazComputationSink() { public void onBazResult(Baz result) { synchronized (notifier) { notifier.set(result); notifier.notify(); } } }); } }); }


Bueno, existe la solución simple de hacer algo como:

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

Por supuesto, esto supone que su resultado de Baz nunca será nulo ...


Esto es completamente simple con RxJava 2.x:

try { Baz baz = Single.create((SingleEmitter<Baz> emitter) -> doSomethingAsync(fooArg, barArg, result -> emitter.onSuccess(result))) .toFuture().get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }

O sin notación Lambda:

Baz baz = Single.create(new SingleOnSubscribe<Baz>() { @Override public void subscribe(SingleEmitter<Baz> emitter) { doSomethingAsync(fooArg, barArg, new BazComputationSink() { @Override public void onBazResult(Baz result) { emitter.onSuccess(result); } }); } }).toFuture().get();

Aún más simple:

Baz baz = Single.create((SingleEmitter<Baz> emitter) -> doSomethingAsync(fooArg, barArg, result -> emitter.onSuccess(result))) .blockingGet();


La biblioteca de google guava tiene un SettableFuture fácil de usar que hace que este problema sea muy simple (alrededor de 10 líneas de código).

public class ImplementingThing { public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) { try { return doSomething(fooArg, barArg).get(); } catch (Exception e) { throw new RuntimeException("Oh dear"); } }; public Future<Baz> doSomething(Foo fooArg, Bar barArg) { final SettableFuture<Baz> future = new SettableFuture<Baz>(); doSomethingAsync(fooArg, barArg, new BazComputationSink() { @Override public void onBazResult(Baz result) { future.set(result); } }); return future; }; // Everything below here is just mock stuff to make the example work, // so you can copy it into your IDE and see it run. public static class Baz {} public static class Foo {} public static class Bar {} public static interface BazComputationSink { public void onBazResult(Baz result); } public void doSomethingAsync(Foo fooArg, Bar barArg, final BazComputationSink sink) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } Baz baz = new Baz(); sink.onBazResult(baz); } }).start(); }; public static void main(String[] args) { System.err.println("Starting Main"); System.err.println((new ImplementingThing()).doSomethingAndBlock(null, null)); System.err.println("Ending Main"); }


Usando su propia implementación Futura:

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

La solución crea un CountDownLatch internamente que se borra una vez que se recibe la devolución de llamada. Si el usuario llama a get, CountDownLatch se usa para bloquear el hilo de llamada hasta que el cálculo se complete y llame a la devolución de llamada de onBazResult. CountDownLatch asegurará que si la devolución de llamada ocurre antes de llamar a get (), el método get () devolverá inmediatamente con un resultado.