View Javadoc
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  
19  import org.apache.commons.lang3.function.FailableConsumer;
20  import org.apache.commons.lang3.function.FailableSupplier;
21  
22  /**
23   * This class provides a generic implementation of the lazy initialization pattern.
24   *
25   * <p>
26   * 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
27   * 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
28   * creation of this object until it is really needed. This is a use case for the lazy initialization pattern.
29   * </p>
30   * <p>
31   * 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
32   * edition, item 71. The class already implements all necessary synchronization. A concrete subclass has to implement the {@code initialize()} method, which
33   * actually creates the wrapped data object.
34   * </p>
35   * <p>
36   * 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
37   * this class, a subclass of {@link LazyInitializer} has to be created:
38   * </p>
39   *
40   * <pre>{@code
41   * public class ComplexObjectInitializer extends LazyInitializer<ComplexObject> {
42   *     &#064;Override
43   *     protected ComplexObject initialize() {
44   *         return new ComplexObject();
45   *     }
46   * }
47   * }
48   * </pre>
49   *
50   * <p>
51   * 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
52   * like this:
53   * </p>
54   *
55   * <pre>
56   * // Create an instance of the lazy initializer
57   * ComplexObjectInitializer initializer = new ComplexObjectInitializer();
58   * ...
59   * // When the object is actually needed:
60   * ComplexObject cobj = initializer.get();
61   * </pre>
62   *
63   * <p>
64   * If multiple threads call the {@code get()} method when the object has not yet been created, they are blocked until initialization completes. The algorithm
65   * 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()}
66   * method are pretty fast because no synchronization is needed (only an access to a <b>volatile</b> member field).
67   * </p>
68   *
69   * @since 3.0
70   * @param <T> the type of the object managed by the initializer.
71   */
72  public class LazyInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {
73  
74      /**
75       * Builds a new instance.
76       *
77       * @param <T> the type of the object managed by the initializer.
78       * @param <I> the type of the initializer managed by this builder.
79       * @since 3.14.0
80       */
81      public static class Builder<I extends LazyInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, ConcurrentException> {
82  
83          /**
84           * Constructs a new instance.
85           */
86          public Builder() {
87              // empty
88          }
89  
90          @SuppressWarnings("unchecked")
91          @Override
92          public I get() {
93              return (I) new LazyInitializer(getInitializer(), getCloser());
94          }
95  
96      }
97  
98      /**
99       * A unique value indicating an un-initialized instance.
100      */
101     private static final Object NO_INIT = new Object();
102 
103     /**
104      * Creates a new builder.
105      *
106      * @param <T> the type of object to build.
107      * @return a new builder.
108      * @since 3.14.0
109      */
110     public static <T> Builder<LazyInitializer<T>, T> builder() {
111         return new Builder<>();
112     }
113 
114     /** Stores the managed object. */
115     @SuppressWarnings("unchecked")
116     private volatile T object = (T) NO_INIT;
117 
118     /**
119      * Constructs a new instance.
120      */
121     public LazyInitializer() {
122         // empty
123     }
124 
125     /**
126      * Constructs a new instance.
127      *
128      * @param initializer the initializer supplier called by {@link #initialize()}.
129      * @param closer the closer consumer called by {@link #close()}.
130      */
131     private LazyInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer) {
132         super(initializer, closer);
133     }
134 
135     /**
136      * 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.
137      *
138      * @return the object initialized by this {@link LazyInitializer}
139      * @throws ConcurrentException if an error occurred during initialization of the object
140      */
141     @Override
142     public T get() throws ConcurrentException {
143         // use a temporary variable to reduce the number of reads of the
144         // volatile field
145         T result = object;
146 
147         if (result == NO_INIT) {
148             synchronized (this) {
149                 result = object;
150                 if (result == NO_INIT) {
151                     object = result = initialize();
152                 }
153             }
154         }
155 
156         return result;
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     @Override
163     protected ConcurrentException getTypedException(final Exception e) {
164         return new ConcurrentException(e);
165     }
166 
167     /**
168      * Tests whether this instance is initialized. Once initialized, always returns true.
169      *
170      * @return whether this instance is initialized. Once initialized, always returns true.
171      * @since 3.14.0
172      */
173     @Override
174     public boolean isInitialized() {
175         return object != NO_INIT;
176     }
177 
178 }