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 * https://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 * @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 <strong>volatile</strong> member field).
67 * </p>
68 *
69 * @param <T> the type of the object managed by the initializer.
70 * @since 3.0
71 */
72 public class LazyInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {
73
74 /**
75 * Builds a new instance.
76 *
77 * @param <T> The type of results supplied by this builder.
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 * Gets 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 if (result == NO_INIT) {
147 synchronized (this) {
148 result = object;
149 if (result == NO_INIT) {
150 object = result = initialize();
151 }
152 }
153 }
154 return result;
155 }
156
157 /**
158 * {@inheritDoc}
159 */
160 @Override
161 protected ConcurrentException getTypedException(final Exception e) {
162 return new ConcurrentException(e);
163 }
164
165 /**
166 * Tests whether this instance is initialized. Once initialized, always returns true.
167 *
168 * @return whether this instance is initialized. Once initialized, always returns true.
169 * @since 3.14.0
170 */
171 @Override
172 public boolean isInitialized() {
173 return object != NO_INIT;
174 }
175
176 }