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 import java.util.concurrent.locks.LockSupport;
21
22 import org.apache.commons.lang3.exception.ExceptionUtils;
23 import org.apache.commons.lang3.function.FailableConsumer;
24 import org.apache.commons.lang3.function.FailableSupplier;
25
26 /**
27 * A specialized {@link ConcurrentInitializer} implementation which is similar
28 * to {@link AtomicInitializer}, but ensures that the {@link #initialize()}
29 * method is called only once.
30 *
31 * <p>
32 * As {@link AtomicInitializer} this class is based on atomic variables, so it
33 * can create an object under concurrent access without synchronization.
34 * However, it implements an additional check to guarantee that the
35 * {@link #initialize()} method which actually creates the object cannot be
36 * called multiple times.
37 * </p>
38 * <p>
39 * Because of this additional check this implementation is slightly less
40 * efficient than {@link AtomicInitializer}, but if the object creation in the
41 * {@code initialize()} method is expensive or if multiple invocations of
42 * {@code initialize()} are problematic, it is the better alternative.
43 * </p>
44 * <p>
45 * From its semantics this class has the same properties as
46 * {@link LazyInitializer}. It is a "save" implementation of the lazy
47 * initializer pattern. Comparing both classes in terms of efficiency is
48 * difficult because which one is faster depends on multiple factors. Because
49 * {@link AtomicSafeInitializer} does not use synchronization at all it probably
50 * outruns {@link LazyInitializer}, at least under low or moderate concurrent
51 * access. Developers should run their own benchmarks on the expected target
52 * platform to decide which implementation is suitable for their specific use
53 * case.
54 * </p>
55 *
56 * @param <T> The type of the object managed by this initializer class
57 * @since 3.0
58 */
59 public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {
60
61 /**
62 * Builds a new instance.
63 *
64 * @param <T> The type of results supplied by this builder.
65 * @param <I> The type of the initializer managed by this builder.
66 * @since 3.14.0
67 */
68 public static class Builder<I extends AtomicSafeInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, ConcurrentException> {
69
70 /**
71 * Constructs a new instance.
72 */
73 public Builder() {
74 // empty
75 }
76
77 @SuppressWarnings("unchecked")
78 @Override
79 public I get() {
80 return (I) new AtomicSafeInitializer(getInitializer(), getCloser());
81 }
82
83 }
84
85 private static final Object NO_INIT = new Object();
86
87 /**
88 * Creates a new builder.
89 *
90 * @param <T> The type of object to build.
91 * @return A new builder.
92 * @since 3.14.0
93 */
94 public static <T> Builder<AtomicSafeInitializer<T>, T> builder() {
95 return new Builder<>();
96 }
97
98 /** A guard which ensures that initialize() is called only once. */
99 private final AtomicReference<AtomicSafeInitializer<T>> factory = new AtomicReference<>();
100
101 /** Holds the reference to the managed object. */
102 private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
103
104 /**
105 * Constructs a new instance.
106 */
107 public AtomicSafeInitializer() {
108 // empty
109 }
110
111 /**
112 * Constructs a new instance.
113 *
114 * @param initializer The initializer supplier called by {@link #initialize()}.
115 * @param closer The closer consumer called by {@link #close()}.
116 */
117 private AtomicSafeInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer) {
118 super(initializer, closer);
119 }
120
121 /**
122 * Gets (and initialize, if not initialized yet) the required object.
123 *
124 * @return lazily initialized object.
125 * @throws ConcurrentException Thrown if the initialization of the object causes an exception.
126 */
127 @Override
128 public final T get() throws ConcurrentException {
129 T result;
130 while ((result = reference.get()) == getNoInit()) {
131 if (factory.compareAndSet(null, this)) {
132 try {
133 reference.set(initialize());
134 } catch (final Throwable t) {
135 // Allow retry on failure; otherwise callers spin forever.
136 factory.set(null);
137 // Rethrow preserving original semantics: unchecked as-is, checked wrapped.
138 final Throwable checked = ExceptionUtils.throwUnchecked(t);
139 throw checked instanceof ConcurrentException ? (ConcurrentException) checked : new ConcurrentException(checked);
140 }
141 } else {
142 // Another thread won the CAS; park 1 ms rather than busy-waiting.
143 LockSupport.parkNanos(1_000_000L);
144 }
145 }
146 return result;
147 }
148
149 /** Gets the internal no-init object cast for this instance. */
150 @SuppressWarnings("unchecked")
151 private T getNoInit() {
152 return (T) NO_INIT;
153 }
154
155 /**
156 * {@inheritDoc}
157 */
158 @Override
159 protected ConcurrentException getTypedException(final Exception e) {
160 return new ConcurrentException(e);
161 }
162
163 /**
164 * Tests whether this instance is initialized. Once initialized, always returns true.
165 *
166 * @return whether this instance is initialized. Once initialized, always returns true.
167 * @since 3.14.0
168 */
169 @Override
170 public boolean isInitialized() {
171 return reference.get() != NO_INIT;
172 }
173 }