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 java.util.concurrent.atomic.AtomicReference;
20
21 import org.apache.commons.lang3.function.FailableConsumer;
22 import org.apache.commons.lang3.function.FailableSupplier;
23
24 /**
25 * A specialized implementation of the {@link ConcurrentInitializer} interface
26 * based on an {@link AtomicReference} variable.
27 *
28 * <p>
29 * This class maintains a member field of type {@link AtomicReference}. It
30 * implements the following algorithm to create and initialize an object in its
31 * {@link #get()} method:
32 * </p>
33 * <ul>
34 * <li>First it is checked whether the {@link AtomicReference} variable contains
35 * already a value. If this is the case, the value is directly returned.</li>
36 * <li>Otherwise the {@link #initialize()} method is called. This method must be
37 * defined in concrete subclasses to actually create the managed object.</li>
38 * <li>After the object was created by {@link #initialize()} it is checked
39 * whether the {@link AtomicReference} variable is still undefined. This has to
40 * be done because in the meantime another thread may have initialized the
41 * object. If the reference is still empty, the newly created object is stored
42 * in it and returned by this method.</li>
43 * <li>Otherwise the value stored in the {@link AtomicReference} is returned.</li>
44 * </ul>
45 * <p>
46 * Because atomic variables are used this class does not need any
47 * synchronization. So there is no danger of deadlock, and access to the managed
48 * object is efficient. However, if multiple threads access the {@code
49 * AtomicInitializer} object before it has been initialized almost at the same
50 * time, it can happen that {@link #initialize()} is called multiple times. The
51 * algorithm outlined above guarantees that {@link #get()} always returns the
52 * same object though.
53 * </p>
54 * <p>
55 * Compared with the {@link LazyInitializer} class, this class can be more
56 * efficient because it does not need synchronization. The drawback is that the
57 * {@link #initialize()} method can be called multiple times which may be
58 * problematic if the creation of the managed object is expensive. As a rule of
59 * thumb this initializer implementation is preferable if there are not too many
60 * threads involved and the probability that multiple threads access an
61 * uninitialized object is small. If there is high parallelism,
62 * {@link LazyInitializer} is more appropriate.
63 * </p>
64 *
65 * @param <T> the type of the object managed by this initializer class.
66 * @since 3.0
67 */
68 public class AtomicInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {
69
70 /**
71 * Builds a new instance.
72 *
73 * @param <T> The type of results supplied by this builder.
74 * @param <I> The type of the initializer managed by this builder.
75 * @since 3.14.0
76 */
77 public static class Builder<I extends AtomicInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, ConcurrentException> {
78
79 /**
80 * Constructs a new instance.
81 */
82 public Builder() {
83 // empty
84 }
85
86 @SuppressWarnings("unchecked")
87 @Override
88 public I get() {
89 return (I) new AtomicInitializer(getInitializer(), getCloser());
90 }
91
92 }
93
94 private static final Object NO_INIT = new Object();
95
96 /**
97 * Creates a new builder.
98 *
99 * @param <T> the type of object to build.
100 * @return a new builder.
101 * @since 3.14.0
102 */
103 public static <T> Builder<AtomicInitializer<T>, T> builder() {
104 return new Builder<>();
105 }
106
107 /** Holds the reference to the managed object. */
108 private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
109
110 /**
111 * Constructs a new instance.
112 */
113 public AtomicInitializer() {
114 // empty
115 }
116
117 /**
118 * Constructs a new instance.
119 *
120 * @param initializer the initializer supplier called by {@link #initialize()}.
121 * @param closer the closer consumer called by {@link #close()}.
122 */
123 private AtomicInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer) {
124 super(initializer, closer);
125 }
126
127 /**
128 * Gets the object managed by this initializer. The object is created if it is not available yet and stored internally. This method always returns the same
129 * object.
130 *
131 * @return the object created by this {@link AtomicInitializer}.
132 * @throws ConcurrentException if an error occurred during initialization of the object.
133 */
134 @Override
135 public T get() throws ConcurrentException {
136 T result = reference.get();
137 if (result == getNoInit()) {
138 result = initialize();
139 if (!reference.compareAndSet(getNoInit(), result)) {
140 // another thread has initialized the reference
141 result = reference.get();
142 }
143 }
144 return result;
145 }
146
147 /** Gets the internal no-init object cast for this instance. */
148 @SuppressWarnings("unchecked")
149 private T getNoInit() {
150 return (T) NO_INIT;
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 protected ConcurrentException getTypedException(final Exception e) {
158 return new ConcurrentException(e);
159 }
160
161 /**
162 * Tests whether this instance is initialized. Once initialized, always returns true.
163 *
164 * @return whether this instance is initialized. Once initialized, always returns true.
165 * @since 3.14.0
166 */
167 @Override
168 public boolean isInitialized() {
169 return reference.get() != NO_INIT;
170 }
171 }