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 java.util.concurrent.atomic.AtomicReference;
20  
21  /**
22   * <p>
23   * A specialized implementation of the {@code ConcurrentInitializer} interface
24   * based on an {@link AtomicReference} variable.
25   * </p>
26   * <p>
27   * This class maintains a member field of type {@code AtomicReference}. It
28   * implements the following algorithm to create and initialize an object in its
29   * {@link #get()} method:
30   * </p>
31   * <ul>
32   * <li>First it is checked whether the {@code AtomicReference} variable contains
33   * already a value. If this is the case, the value is directly returned.</li>
34   * <li>Otherwise the {@link #initialize()} method is called. This method must be
35   * defined in concrete subclasses to actually create the managed object.</li>
36   * <li>After the object was created by {@link #initialize()} it is checked
37   * whether the {@code AtomicReference} variable is still undefined. This has to
38   * be done because in the meantime another thread may have initialized the
39   * object. If the reference is still empty, the newly created object is stored
40   * in it and returned by this method.</li>
41   * <li>Otherwise the value stored in the {@code AtomicReference} is returned.</li>
42   * </ul>
43   * <p>
44   * Because atomic variables are used this class does not need any
45   * synchronization. So there is no danger of deadlock, and access to the managed
46   * object is efficient. However, if multiple threads access the {@code
47   * AtomicInitializer} object before it has been initialized almost at the same
48   * time, it can happen that {@link #initialize()} is called multiple times. The
49   * algorithm outlined above guarantees that {@link #get()} always returns the
50   * same object though.
51   * </p>
52   * <p>
53   * Compared with the {@link LazyInitializer} class, this class can be more
54   * efficient because it does not need synchronization. The drawback is that the
55   * {@link #initialize()} method can be called multiple times which may be
56   * problematic if the creation of the managed object is expensive. As a rule of
57   * thumb this initializer implementation is preferable if there are not too many
58   * threads involved and the probability that multiple threads access an
59   * uninitialized object is small. If there is high parallelism,
60   * {@link LazyInitializer} is more appropriate.
61   * </p>
62   *
63   * @since 3.0
64   * @version $Id: AtomicInitializer.java 1583482 2014-03-31 22:54:57Z niallp $
65   * @param <T> the type of the object managed by this initializer class
66   */
67  public abstract class AtomicInitializer<T> implements ConcurrentInitializer<T> {
68      /** Holds the reference to the managed object. */
69      private final AtomicReference<T> reference = new AtomicReference<T>();
70  
71      /**
72       * Returns the object managed by this initializer. The object is created if
73       * it is not available yet and stored internally. This method always returns
74       * the same object.
75       *
76       * @return the object created by this {@code AtomicInitializer}
77       * @throws ConcurrentException if an error occurred during initialization of
78       * the object
79       */
80      @Override
81      public T get() throws ConcurrentException {
82          T result = reference.get();
83  
84          if (result == null) {
85              result = initialize();
86              if (!reference.compareAndSet(null, result)) {
87                  // another thread has initialized the reference
88                  result = reference.get();
89              }
90          }
91  
92          return result;
93      }
94  
95      /**
96       * Creates and initializes the object managed by this {@code
97       * AtomicInitializer}. This method is called by {@link #get()} when the
98       * managed object is not available yet. An implementation can focus on the
99       * creation of the object. No synchronization is needed, as this is already
100      * handled by {@code get()}. As stated by the class comment, it is possible
101      * that this method is called multiple times.
102      *
103      * @return the managed data object
104      * @throws ConcurrentException if an error occurs during object creation
105      */
106     protected abstract T initialize() throws ConcurrentException;
107 }