|
|
@@ -0,0 +1,128 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/everything
|
|
|
+ */
|
|
|
+
|
|
|
+package net.ranides.assira.generic;
|
|
|
+
|
|
|
+import java.util.concurrent.Callable;
|
|
|
+import java.util.concurrent.atomic.AtomicReference;
|
|
|
+import java.util.function.Supplier;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @param <T>
|
|
|
+ */
|
|
|
+public final class LazyReference {
|
|
|
+
|
|
|
+ private static enum State { UNDEFINED_1, UNDEFINED_2 }
|
|
|
+
|
|
|
+ private LazyReference() {
|
|
|
+ /* utility class */
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialization is unsynchronized. There is no guarantee about thread-safety.
|
|
|
+ * @param <T>
|
|
|
+ * @param supplier
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+
|
|
|
+ public static <T> Supplier<T> unique(Supplier<T> supplier) {
|
|
|
+ return new NonLocking<>(supplier);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialization is done once per thread. Every thread will have different thread-local object.
|
|
|
+ * @param <T>
|
|
|
+ * @param supplier
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static <T> Supplier<T> local(Supplier<T> supplier) {
|
|
|
+ return ThreadLocal.withInitial(supplier)::get;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialization is synchronized using correctly implemented "double checked locking".
|
|
|
+ * @param <T>
|
|
|
+ * @param supplier
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static <T> Supplier<T> shared(Supplier<T> supplier) {
|
|
|
+ return new Shared<>(supplier);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialization is lock-free. There is a chance of spurious "double initialization".
|
|
|
+ * @param <T>
|
|
|
+ * @param supplier
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static <T> Supplier<T> concurent(Supplier<T> supplier) {
|
|
|
+ AtomicReference<Object> ref = new AtomicReference<>(State.UNDEFINED_1);
|
|
|
+ return () -> {
|
|
|
+ if( ref.compareAndSet(State.UNDEFINED_1, State.UNDEFINED_2) ) {
|
|
|
+ ref.set(supplier.get() );
|
|
|
+ return (T)ref.get();
|
|
|
+ }
|
|
|
+ if( ref.compareAndSet(State.UNDEFINED_2, State.UNDEFINED_1) ) {
|
|
|
+ ref.set(supplier.get() );
|
|
|
+ return (T)ref.get();
|
|
|
+ }
|
|
|
+ return (T)ref.get();
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class NonLocking<T> implements Supplier<T> {
|
|
|
+
|
|
|
+ private final Supplier<T> supplier;
|
|
|
+
|
|
|
+ private T ret;
|
|
|
+
|
|
|
+ public NonLocking(Supplier<T> supplier) {
|
|
|
+ this.supplier = supplier;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T get() {
|
|
|
+ if( ret == null) {
|
|
|
+ ret = supplier.get();
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Shared<T> implements Supplier<T> {
|
|
|
+
|
|
|
+ // works in JDK5 +
|
|
|
+ // http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
|
|
|
+
|
|
|
+ private final Supplier<T> supplier;
|
|
|
+
|
|
|
+ private volatile T ref = null;
|
|
|
+
|
|
|
+ public Shared(Supplier<T> supplier) {
|
|
|
+ this.supplier = supplier;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T get() {
|
|
|
+ T temp = ref;
|
|
|
+ if(null == temp) {
|
|
|
+ synchronized(this) {
|
|
|
+ if(null == ref) {
|
|
|
+ ref = temp = supplier.get();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|