AtomicSafeInitializer.java

  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. import java.util.concurrent.atomic.AtomicReference;

  19. import org.apache.commons.lang3.function.FailableConsumer;
  20. import org.apache.commons.lang3.function.FailableSupplier;

  21. /**
  22.  * A specialized {@link ConcurrentInitializer} implementation which is similar
  23.  * to {@link AtomicInitializer}, but ensures that the {@link #initialize()}
  24.  * method is called only once.
  25.  *
  26.  * <p>
  27.  * As {@link AtomicInitializer} this class is based on atomic variables, so it
  28.  * can create an object under concurrent access without synchronization.
  29.  * However, it implements an additional check to guarantee that the
  30.  * {@link #initialize()} method which actually creates the object cannot be
  31.  * called multiple times.
  32.  * </p>
  33.  * <p>
  34.  * Because of this additional check this implementation is slightly less
  35.  * efficient than {@link AtomicInitializer}, but if the object creation in the
  36.  * {@code initialize()} method is expensive or if multiple invocations of
  37.  * {@code initialize()} are problematic, it is the better alternative.
  38.  * </p>
  39.  * <p>
  40.  * From its semantics this class has the same properties as
  41.  * {@link LazyInitializer}. It is a &quot;save&quot; implementation of the lazy
  42.  * initializer pattern. Comparing both classes in terms of efficiency is
  43.  * difficult because which one is faster depends on multiple factors. Because
  44.  * {@link AtomicSafeInitializer} does not use synchronization at all it probably
  45.  * outruns {@link LazyInitializer}, at least under low or moderate concurrent
  46.  * access. Developers should run their own benchmarks on the expected target
  47.  * platform to decide which implementation is suitable for their specific use
  48.  * case.
  49.  * </p>
  50.  *
  51.  * @since 3.0
  52.  * @param <T> the type of the object managed by this initializer class
  53.  */
  54. public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, ConcurrentException> {

  55.     /**
  56.      * Builds a new instance.
  57.      *
  58.      * @param <T> the type of the object managed by the initializer.
  59.      * @param <I> the type of the initializer managed by this builder.
  60.      * @since 3.14.0
  61.      */
  62.     public static class Builder<I extends AtomicSafeInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, ConcurrentException> {

  63.         /**
  64.          * Constructs a new instance.
  65.          */
  66.         public Builder() {
  67.             // empty
  68.         }

  69.         @SuppressWarnings("unchecked")
  70.         @Override
  71.         public I get() {
  72.             return (I) new AtomicSafeInitializer(getInitializer(), getCloser());
  73.         }

  74.     }

  75.     private static final Object NO_INIT = new Object();

  76.     /**
  77.      * Creates a new builder.
  78.      *
  79.      * @param <T> the type of object to build.
  80.      * @return a new builder.
  81.      * @since 3.14.0
  82.      */
  83.     public static <T> Builder<AtomicSafeInitializer<T>, T> builder() {
  84.         return new Builder<>();
  85.     }

  86.     /** A guard which ensures that initialize() is called only once. */
  87.     private final AtomicReference<AtomicSafeInitializer<T>> factory = new AtomicReference<>();

  88.     /** Holds the reference to the managed object. */
  89.     private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());

  90.     /**
  91.      * Constructs a new instance.
  92.      */
  93.     public AtomicSafeInitializer() {
  94.         // empty
  95.     }

  96.     /**
  97.      * Constructs a new instance.
  98.      *
  99.      * @param initializer the initializer supplier called by {@link #initialize()}.
  100.      * @param closer the closer consumer called by {@link #close()}.
  101.      */
  102.     private AtomicSafeInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer) {
  103.         super(initializer, closer);
  104.     }

  105.     /**
  106.      * Gets (and initialize, if not initialized yet) the required object
  107.      *
  108.      * @return lazily initialized object
  109.      * @throws ConcurrentException if the initialization of the object causes an
  110.      * exception
  111.      */
  112.     @Override
  113.     public final T get() throws ConcurrentException {
  114.         T result;

  115.         while ((result = reference.get()) == getNoInit()) {
  116.             if (factory.compareAndSet(null, this)) {
  117.                 reference.set(initialize());
  118.             }
  119.         }

  120.         return result;
  121.     }

  122.     /** Gets the internal no-init object cast for this instance. */
  123.     @SuppressWarnings("unchecked")
  124.     private T getNoInit() {
  125.         return (T) NO_INIT;
  126.     }

  127.     /**
  128.      * {@inheritDoc}
  129.      */
  130.     @Override
  131.     protected ConcurrentException getTypedException(final Exception e) {
  132.         return new ConcurrentException(e);
  133.     }

  134.     /**
  135.      * Tests whether this instance is initialized. Once initialized, always returns true.
  136.      *
  137.      * @return whether this instance is initialized. Once initialized, always returns true.
  138.      * @since 3.14.0
  139.      */
  140.     @Override
  141.     public boolean isInitialized() {
  142.         return reference.get() != NO_INIT;
  143.     }
  144. }