package the8472.utils.concurrent; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Predicate; public class SerializedTaskExecutor { public static <T> Consumer<T> runSerialized(Consumer<T> task) { AtomicBoolean lock = new AtomicBoolean(); Queue<T> q = new ConcurrentLinkedQueue<>(); Predicate<T> tryRun = (T toTry) -> { boolean success = false; while(lock.compareAndSet(false, true)) { try { if(toTry != null) { task.accept(toTry); success = true; } T other; while((other = q.poll()) != null) task.accept(other); } finally { lock.set(false); } if(q.peek() == null) break; } return success; }; return (T r) -> { // attempt to execute on current thread if(lock.get() == false && tryRun.test(r)) return;// success // execution on current thread failed, enqueue q.add(r); // try again in case other thread ceased draining the queue if (lock.get() == false) tryRun.test(null); }; } public static Runnable onceMore(Runnable loopBody) { AtomicInteger lock = new AtomicInteger(); return () -> { // request execution of the runnable int current = lock.incrementAndGet(); // another thread is executing if(current > 1) return; do { loopBody.run(); current = lock.addAndGet(Math.negateExact(current)); } while(current > 0); }; } }