11
\$\begingroup\$

See Wikipedia - Multiton Pattern for details on the intent.

Here's the class:

/**
 * Holds a thread-safe map of unique create-once items.
 * 
 * Contract:
 * 
 * Only one object will be made for each key presented.
 * 
 * Thread safe.
 *
 * @param <K>
 * @param <V>
 */
public class Multiton<K, V> {
  // Map from the key to the futures of the items.
  private final ConcurrentMap<K, Future<V>> multitons = new ConcurrentHashMap<K, Future<V>>();
  // A Prayer is used to construct the Callable that is attached to the FutureTask.
  private final Prayer prayer;

  public Multiton(Creator<K, V> creator) {
    // Create the prayer to the creator.
    this.prayer = new Prayer(creator);
  }

  /**
   * There can be only one.
   * 
   * Use a FutureTask to do the creation to ensure only one construction.
   * 
   * @param key
   * @return
   * @throws InterruptedException
   * @throws ExecutionException 
   */
  public V getInstance(final K key) throws InterruptedException, ExecutionException {
    // Already made?
    Future<V> f = multitons.get(key);
    if (f == null) {
      // Plan the future but do not create as yet.
      FutureTask<V> ft = new FutureTask<V>(prayer.pray(key));
      // Store it.
      f = multitons.putIfAbsent(key, ft);
      if (f == null) {
        // It was successfully stored - it is the first (and only)
        f = ft;
        // Make it happen.
        ft.run();
      }
    }
    // Wait for it to finish construction and return the constructed.
    return f.get();
  }

  /**
   * Use a Prayer to pass the key to the Creator.
   *
   * @param <K>
   * @param <V>
   */
  private class Prayer {
    private final Creator<K, V> creator;

    public Prayer(Creator<K, V> creator) {
      this.creator = creator;
    }

    public Callable<V> pray(final K key) {
      return new Callable<V>() {

        @Override
        public V call() throws Exception {
          return creator.create(key);
        }

      };
    }

  }

  /**
   * User provides one of these to do the construction.
   * 
   * @param <K>
   * @param <V> 
   */
  public abstract static class Creator<K, V> {
    // Return a new item under the key.
    abstract V create(K key) throws ExecutionException;

  }

}

I use it to ensure I only create one JAXBContex for each class:

/**
 * Create just one JAXB context for each class.
 */
private static final Multiton<Class, JAXBContext> contexts = new Multiton<Class, JAXBContext>(new Creator<Class, JAXBContext>() {

  @Override
  JAXBContext create(Class key) throws ExecutionException {
    try {
      // Get it out of my Multiton
      return JAXBContext.newInstance(key);
    } catch (JAXBException ex) {
      // Pass it back as an Execution Exception.
      throw new ExecutionException("Failed to create JAXB Context for class "+key.getName(), ex);
    }
  }

});

/**
 * Uses a Multiton to create a JAXB context for the object.
 * 
 * @param it - The object to create the context for.
 * @return - the unique JAXB context.
 * @throws InterruptedException - Interrupted.
 * @throws ExecutionException - Something else went wrong (cause is wrapped)
 */
private static JAXBContext createJAXBContext(Object it) throws InterruptedException, ExecutionException {
  return contexts.getInstance(it.getClass());
}

My Questions:

  1. Is this a Multiton as described by the article? I.e. Will it only ever create just one instance of the object?
  2. Is it thread-safe?
  3. Am I being offensive using a Prayer object to request an object from the Creator?
  4. If the answer to 3 is no - should I make my create method of the Creator take a Prayer as a parameter rather than a K key - just for the belly laugh of course?

NB: The idea of using a FutureTask came from a nice answer by assylias on thread-safe multitons. All I've really done is make it generic and plumbed in the ability to pass a Creator.

\$\endgroup\$
9
  • \$\begingroup\$ Hmmm, a Prayer class with a prey method. Now that's not the same thing, is it? \$\endgroup\$ Jan 24 '14 at 14:28
  • \$\begingroup\$ I'd call it a 'Factory' class. \$\endgroup\$
    – ChrisW
    Jan 24 '14 at 14:29
  • 2
    \$\begingroup\$ Or we could call the method pray. \$\endgroup\$ Jan 24 '14 at 14:33
  • 3
    \$\begingroup\$ Hmm I thought "multiton" was something that weighted several tons. lol.upvoted. \$\endgroup\$ Jan 24 '14 at 14:56
  • 2
    \$\begingroup\$ @lol.upvote - do I not get a lol.upvote for gratuitous use of methods of communications with deities in Java? \$\endgroup\$ Jan 24 '14 at 15:14
4
\$\begingroup\$

I think your use-case and the Wiki description are different. Technically, the Multiton should return the exact same intance for the exact same key, always, but, because your multitons Map is not a static instance, you can simply create multiple instances of the Multiton and each one will generate different instances for the same key.

This is OK in your case, because you create a private static final anonymous implementation of the class.... so you handle that problem at a higher level.

I think, otherwise, the algorithm is good.... (i.e. it will work as advertised).

There are two changes I can recommend....

  1. Why is the Creator<K,V> a public abstract static class? This is a broken pattern, and it should simply be an interface public interface Creator<K,V>

  2. This is complicated, the rest of the answer is about change 2....

I believe you have unnecessary indirection. Your inner class:

  private class Prayer {
    private final Creator<K, V> creator;

    public Prayer(Creator<K, V> creator) {
      this.creator = creator;
    }

    public Callable<V> pray(final K key) {
      return new Callable<V>() {

        @Override
        public V call() throws Exception {
          return creator.create(key);
        }

      };
    }

  }

This is overkill.... if you make the Prayer class Extend Callable, you win:

  private class Prayer implements Callable<V> {
    private final K key;

    public Prayer(K key) {
      this.key = key;
    }

    @Override
    public V call() throws Exception {
      // creator is accessible as a final field on the Multiton instance.
      return creator.create(key);
    }

  }

Then, instead of keeping a final instance to Prayer in your main class:

private final Prayer prayer;

you should instead hold the simple Creator:

private final Creator<K,V> creator;

And, instead of calling prayer.pray(), you only need do:

FutureTask<V> ft = new FutureTask<V>(new Prayer(key));

Note: It is much better to request a new Prayer(...) than it is to demand one with prayer.pray() !!!


Hope this all makes sense.... I have reworked your original code as:

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Multiton<K, V> {
  // Map from the key to the futures of the items.
  private final ConcurrentMap<K, Future<V>> multitons = new ConcurrentHashMap<K, Future<V>>();
  // A Creator is used to construct the Callable that is attached to the FutureTask.
  private final Creator<K,V> creator;

  public Multiton(Creator<K, V> creator) {
    this.creator = creator;
  }

  /**
   * There can be only one.
   * 
   * Use a FutureTask to do the creation to ensure only one construction.
   * 
   * @param key
   * @return
   * @throws InterruptedException
   * @throws ExecutionException 
   */
  public V getInstance(final K key) throws InterruptedException, ExecutionException {
    // Already made?
    Future<V> f = multitons.get(key);
    if (f == null) {
      // Plan the future but do not create as yet.
      FutureTask<V> ft = new FutureTask<V>(new Prayer(key));
      // Store it.
      f = multitons.putIfAbsent(key, ft);
      if (f == null) {
        // It was successfully stored - it is the first (and only)
        f = ft;
        // Make it happen.
        ft.run();
      }
    }
    // Wait for it to finish construction and return the constructed.
    return f.get();
  }

  /**
   * Use a Prayer to pass the key to the Creator.
   *
   * @param <K>
   * @param <V>
   */
  private class Prayer implements Callable<V> {
    private final K key;

    public Prayer(K key) {
      this.key = key;
    }

    @Override
    public V call() throws Exception {
      return creator.create(key);
    }

  }

  /**
   * User provides one of these to do the construction.
   * 
   * @param <K>
   * @param <V> 
   */
  public interface Creator<K, V> {
    // Return a new item under the key.
    abstract V create(K key) throws ExecutionException;

  }

}
\$\endgroup\$
5
  • \$\begingroup\$ On the abstract/interface point - you are absolutely correct. Fixed that now. \$\endgroup\$ Jan 24 '14 at 15:52
  • \$\begingroup\$ On the Prayer.pray/Prayer implements Callable question - I did it this way to minimise the gratuitous construction of objects. My technique will only create a Callable once for each actual construction. Yours may create more than one under a thread storm. I accept that yours is neater and at a trivial cost so I suspect it is better. \$\endgroup\$ Jan 24 '14 at 15:57
  • \$\begingroup\$ @OldCurmudgeon - respectfully, I disagree with your assertion that you were only creating one Callable in a storm... every time you called pray() you created a Callable... This is no different. \$\endgroup\$
    – rolfl
    Jan 24 '14 at 16:00
  • \$\begingroup\$ Agreed - but pray will only be called when the FutureTask is run so it won't be called if the putIfAbsent finds it present. You are creating a new Prayer for every FutureTask, I create a new Prayer for each Multiton. My argument is trivial because we create a new FutureTask every time anyway so I am not saving anything much and the gain in cleanliness with your solution outweighs that to my mind. \$\endgroup\$ Jan 24 '14 at 16:12
  • \$\begingroup\$ @OldCurmudgeon - hate to labour it... sure, in my version, Prayer is a Callable, in yours, it generates a callable. We both create a new Callable at the exact same point. In your case, new FutureTask<V>(prayer.pray(key)). here prayer.pray(key) creates an anonymous Callable instance. In my case, I do new FutureTask<V>(new Prayer(key)), which is a Callable. It is important you understand your own logic (not because I'm bashing you on this 'just because')... Perhaps join us in the 2nd monitor? \$\endgroup\$
    – rolfl
    Jan 24 '14 at 16:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.