Skip to main content
remove errant <T> on Singleton.
Source Link
rolfl
  • 98.1k
  • 17
  • 220
  • 419
public abstract class Singleton<T>Singleton {
    protected static final Multiton MULTITON = new Multiton();        
}
public abstract class Singleton<T> {
    protected static final Multiton MULTITON = new Multiton();        
}
public abstract class Singleton {
    protected static final Multiton MULTITON = new Multiton();        
}
Bounty Awarded with 50 reputation awarded by janos
Revise TestSingleton a lot.
Source Link
rolfl
  • 98.1k
  • 17
  • 220
  • 419
public abstract class Singleton<T> {
    privateprotected static final Multiton MULTITON = new Multiton();
    
    
    private final Multiton.Creator<T> key;
    
    public Singleton(Class<T> tclass) {
        key = new Multiton.SuppliedCreator<>(tclass, () -> createInstance());
    }
    
    protected abstract T createInstance();
    
    public final T instance() {
        return MULTITON.get(key);
    }

}
public class TestSingleton extends Singleton<String>Singleton {
    
    private static final AtomicIntegerMultiton.Creator<TestSingleton> aiKEY = new AtomicIntegerMultiton.SuppliedCreator<>(TestSingleton.class, TestSingleton::new);
    
    public static final TestSingleton getInstance() {
        super(Stringreturn MULTITON.classget(KEY);
    }
 
    @Override
    protectedprivate final String createInstancename;
    
    private TestSingleton() {
        
        try {
            Thread.sleep(200);
            String name = "Instance " + aiSystem.incrementAndGetcurrentTimeMillis();
            System.out.println("Slow Instance"Instance: " + name);
            Thread.sleep(200);
            return name;
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted ", e);
        }
    }
    
    public@Override
 static void main(String[] argspublic String toString() {
        ExecutorServicereturn servicename;
 = Executors.newFixedThreadPool  }
    
    public static void main(8String[] args); {
        final TestSingletonExecutorService testerservice = new TestSingletonExecutors.newFixedThreadPool(8);
        for (int i = 0; i < 20; i++) {
            service.execute(() -> {System.out.println(testerTestSingleton.instancegetInstance());});
        }
    }

}
public abstract class Singleton<T> {
    private static final Multiton MULTITON = new Multiton();
    
    
    private final Multiton.Creator<T> key;
    
    public Singleton(Class<T> tclass) {
        key = new Multiton.SuppliedCreator<>(tclass, () -> createInstance());
    }
    
    protected abstract T createInstance();
    
    public final T instance() {
        return MULTITON.get(key);
    }

}
public class TestSingleton extends Singleton<String> {
    
    private static final AtomicInteger ai = new AtomicInteger();
    
    public TestSingleton() {
        super(String.class);
    }
 
    @Override
    protected String createInstance() {
        
        try {
            Thread.sleep(200);
            String name = "Instance " + ai.incrementAndGet();
            System.out.println("Slow Instance");
            Thread.sleep(200);
            return name;
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted ", e);
        }
    }
    
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(8);
        final TestSingleton tester = new TestSingleton();
        for (int i = 0; i < 20; i++) {
            service.execute(() -> {System.out.println(tester.instance());});
        }
    }

}
public abstract class Singleton<T> {
    protected static final Multiton MULTITON = new Multiton();        
}
public class TestSingleton extends Singleton {
    
    private static final Multiton.Creator<TestSingleton> KEY = new Multiton.SuppliedCreator<>(TestSingleton.class, TestSingleton::new);
    
    public static final TestSingleton getInstance() {
        return MULTITON.get(KEY);
    }
    
    private final String name;
    
    private TestSingleton() {
        
        try {
            Thread.sleep(200);
            name = "Instance " + System.currentTimeMillis();
            System.out.println("Slow Instance: " + name);
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted ", e);
        }
    }
    
    @Override
    public String toString() {
        return name;
    }
    
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(8);
        for (int i = 0; i < 20; i++) {
            service.execute(() -> {System.out.println(TestSingleton.getInstance());});
        }
    }

}
Source Link
rolfl
  • 98.1k
  • 17
  • 220
  • 419

##ConcurrentMap

As was pointed out in the comments: in the Multiton.get() method:

public <V> V get(final K key, Class<V> type) {
    // Has it run yet?
    Object o = multitons.get(key);
    // A null value means not yet.
    if (o == null) {
        // Use a lambda to only do the create if it is still absent.
        o = multitons.computeIfAbsent(key, k -> k.create());
    }
    return type.cast(o);
}

There's no reason to have the double-check. The computeIfAbsent already does that:

public <V> V get(final K key, Class<V> type) {
    return type.cast(multitons.computeIfAbsent(key, k -> k.create()));
}

##Generics

I have taken, and played with your code. It became apparent that there is no reason to have a generic type on the Multiton class. You only really use it as Multiton<Creator>, and that's the broadest sense of the Generics anyway. The more I played with it, though, the more I realized that your logic is in the wrong place. The right place would be to have more in the Multiton, and to get rid of the Singleton class entirely.... There is no reason for the Singleton interface. It creates a lot of ugliness anyway, because the interface cannot have private static members, so the Multiton instance needs to be non-private, making it a resource leak.

Consider the following Multiton:

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

/**
 * A classic Multiton making use of lambdas to delay the create.
 *
 *
 * @author OldCurmudgeon
 * @param <K> - The type that can create.
 */
public class Multiton {

    /**
     * The keys must be capable of creating their values.
     */
    public interface Creator<V> {
        public Object create();
        public V cast(Object value);
    }
    
    public static abstract class TypedCreator<A> implements Creator<A> {
        private final Class<A> tclass;
        public TypedCreator(Class<A> tclass) {
            this.tclass = tclass;
        }
        
        @Override
        public final A cast(Object value) {
            return tclass.cast(value);
        }
    }

    public static final class SuppliedCreator<A> extends TypedCreator<A> {
        private final Supplier<A> supplier;
        public SuppliedCreator(Class<A> tclass, Supplier<A> supplier) {
            super(tclass);
            this.supplier = supplier;
        }
        
        @Override
        public Object create() {
            return supplier.get();
        }
    }

    /**
     * The storage.
     *
     * Store only Object because they must all be different types.
     */
    private final ConcurrentMap<Creator<?>, Object> multitons = new ConcurrentHashMap<>();

    /**
     * The getter.
     *
     * @param <V> - The type of the value that should be returned.
     * @param key - The unique key behind which the value is to be stored.
     * @return - The value stored (and perhaps created) behind the key.
     */
    public <V, C extends Multiton.Creator<V>> V get(final C key) {
        return key.cast(multitons.computeIfAbsent(key, k -> k.create()));
    }
}

Of particular importance, note:

  1. there's no generic type for the Multiton. This is expected. All it needs is the public get() method, and the generics for that method are determined by the call arguments.... which is point 2....
  2. the generic type of the values are declared on the Creator (which is also the key). In other words, each key knows what the type of the value should be. This means that the generic type is 'recorded' at create time, rather than retrieve time. There's no need to pass a class in to the get() call since the class is actually part of the Creator.
  3. I created 2 'helper' classes that are Creator instances. These classes TypedCreator and SuppliedCreator are simpler ways to actually create anonymous, or Java-8 based Creators.

Note that I can't see a convenient way to use a Singleton interface. The interface depends on a shared instance of the Multiton, so as a result, there needs to be some static data in the Singleton. As a consequence, an interface is the wrong language structure to use. An abstract class may be better. Still, the usage examples for the Multiton above are simple (in some common 'utility' class, or somewhere shared):

public static final Multiton MULTITON = new Multiton();

Then, whenever you have a need for a Singleton:

public static final Multiton.Creator<String> HARDWORK = new Multiton.SuppliedCreator<>(String.class, () -> initializeHardWork());

and then:

String hardwork = MULTITON.get(HARDWORK);

This can be encapsulated in to an abstract Singleton class as:

public abstract class Singleton<T> {
    private static final Multiton MULTITON = new Multiton();
    
    
    private final Multiton.Creator<T> key;
    
    public Singleton(Class<T> tclass) {
        key = new Multiton.SuppliedCreator<>(tclass, () -> createInstance());
    }
    
    protected abstract T createInstance();
    
    public final T instance() {
        return MULTITON.get(key);
    }

}

The Singleton class can then be used in various ways, here's an example:

public class TestSingleton extends Singleton<String> {
    
    private static final AtomicInteger ai = new AtomicInteger();
    
    public TestSingleton() {
        super(String.class);
    }

    @Override
    protected String createInstance() {
        
        try {
            Thread.sleep(200);
            String name = "Instance " + ai.incrementAndGet();
            System.out.println("Slow Instance");
            Thread.sleep(200);
            return name;
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted ", e);
        }
    }
    
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(8);
        final TestSingleton tester = new TestSingleton();
        for (int i = 0; i < 20; i++) {
            service.execute(() -> {System.out.println(tester.instance());});
        }
    }

}