LazyInitializer.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.concurrent;

  18. import org.apache.commons.lang3.function.FailableConsumer;
  19. import org.apache.commons.lang3.function.FailableSupplier;

  20. /**
  21.  * This class provides a generic implementation of the lazy initialization pattern.
  22.  *
  23.  * <p>
  24.  * Sometimes an application has to deal with an object only under certain circumstances, e.g. when the user selects a specific menu item or if a special event
  25.  * is received. If the creation of the object is costly or the consumption of memory or other system resources is significant, it may make sense to defer the
  26.  * creation of this object until it is really needed. This is a use case for the lazy initialization pattern.
  27.  * </p>
  28.  * <p>
  29.  * This abstract base class provides an implementation of the double-check idiom for an instance field as discussed in Joshua Bloch's "Effective Java", 2nd
  30.  * edition, item 71. The class already implements all necessary synchronization. A concrete subclass has to implement the {@code initialize()} method, which
  31.  * actually creates the wrapped data object.
  32.  * </p>
  33.  * <p>
  34.  * As an usage example consider that we have a class {@code ComplexObject} whose instantiation is a complex operation. In order to apply lazy initialization to
  35.  * this class, a subclass of {@link LazyInitializer} has to be created:
  36.  * </p>
  37.  *
  38.  * <pre>{@code
  39.  * public class ComplexObjectInitializer extends LazyInitializer<ComplexObject> {
  40.  *     &#064;Override
  41.  *     protected ComplexObject initialize() {
  42.  *         return new ComplexObject();
  43.  *     }
  44.  * }
  45.  * }
  46.  * </pre>
  47.  *
  48.  * <p>
  49.  * Access to the data object is provided through the {@code get()} method. So, code that wants to obtain the {@code ComplexObject} instance would simply look
  50.  * like this:
  51.  * </p>
  52.  *
  53.  * <pre>
  54.  * // Create an instance of the lazy initializer
  55.  * ComplexObjectInitializer initializer = new ComplexObjectInitializer();
  56.  * ...
  57.  * // When the object is actually needed:
  58.  * ComplexObject cobj = initializer.get();
  59.  * </pre>
  60.  *
  61.  * <p>
  62.  * If multiple threads call the {@code get()} method when the object has not yet been created, they are blocked until initialization completes. The algorithm
  63.  * guarantees that only a single instance of the wrapped object class is created, which is passed to all callers. Once initialized, calls to the {@code get()}
  64.  * method are pretty fast because no synchronization is needed (only an access to a <b>volatile</b> member field).
  65.  * </p>
  66.  *
  67.  * @since 3.0
  68.  * @param <T> the type of the object managed by the initializer.
  69.  */
  70. public class LazyInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {

  71.     /**
  72.      * Builds a new instance.
  73.      *
  74.      * @param <T> the type of the object managed by the initializer.
  75.      * @param <I> the type of the initializer managed by this builder.
  76.      * @since 3.14.0
  77.      */
  78.     public static class Builder<I extends LazyInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, ConcurrentException> {

  79.         /**
  80.          * Constructs a new instance.
  81.          */
  82.         public Builder() {
  83.             // empty
  84.         }

  85.         @SuppressWarnings("unchecked")
  86.         @Override
  87.         public I get() {
  88.             return (I) new LazyInitializer(getInitializer(), getCloser());
  89.         }

  90.     }

  91.     /**
  92.      * A unique value indicating an un-initialized instance.
  93.      */
  94.     private static final Object NO_INIT = new Object();

  95.     /**
  96.      * Creates a new builder.
  97.      *
  98.      * @param <T> the type of object to build.
  99.      * @return a new builder.
  100.      * @since 3.14.0
  101.      */
  102.     public static <T> Builder<LazyInitializer<T>, T> builder() {
  103.         return new Builder<>();
  104.     }

  105.     /** Stores the managed object. */
  106.     @SuppressWarnings("unchecked")
  107.     private volatile T object = (T) NO_INIT;

  108.     /**
  109.      * Constructs a new instance.
  110.      */
  111.     public LazyInitializer() {
  112.         // empty
  113.     }

  114.     /**
  115.      * Constructs a new instance.
  116.      *
  117.      * @param initializer the initializer supplier called by {@link #initialize()}.
  118.      * @param closer the closer consumer called by {@link #close()}.
  119.      */
  120.     private LazyInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer) {
  121.         super(initializer, closer);
  122.     }

  123.     /**
  124.      * Returns the object wrapped by this instance. On first access the object is created. After that it is cached and can be accessed pretty fast.
  125.      *
  126.      * @return the object initialized by this {@link LazyInitializer}
  127.      * @throws ConcurrentException if an error occurred during initialization of the object
  128.      */
  129.     @Override
  130.     public T get() throws ConcurrentException {
  131.         // use a temporary variable to reduce the number of reads of the
  132.         // volatile field
  133.         T result = object;

  134.         if (result == NO_INIT) {
  135.             synchronized (this) {
  136.                 result = object;
  137.                 if (result == NO_INIT) {
  138.                     object = result = initialize();
  139.                 }
  140.             }
  141.         }

  142.         return result;
  143.     }

  144.     /**
  145.      * {@inheritDoc}
  146.      */
  147.     @Override
  148.     protected ConcurrentException getTypedException(final Exception e) {
  149.         return new ConcurrentException(e);
  150.     }

  151.     /**
  152.      * Tests whether this instance is initialized. Once initialized, always returns true.
  153.      *
  154.      * @return whether this instance is initialized. Once initialized, always returns true.
  155.      * @since 3.14.0
  156.      */
  157.     @Override
  158.     public boolean isInitialized() {
  159.         return object != NO_INIT;
  160.     }

  161. }