You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/10/16 06:33:42 UTC

[logging-log4j2] 03/04: Refactor LazyValue into subclasses

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 3946b2febfb7f9cd3b048d0bab0edd3d83a89134
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sun Oct 16 01:31:54 2022 -0500

    Refactor LazyValue into subclasses
    
    Signed-off-by: Matt Sicker <ma...@apache.org>
---
 .../apache/log4j/config/XmlConfigurationTest.java  |  26 ++--
 .../java/org/apache/logging/log4j/LogManager.java  |   4 +-
 .../logging/log4j/message/ThreadDumpMessage.java   |  13 +-
 .../java/org/apache/logging/log4j/util/Lazy.java   |  65 ++++++++
 .../org/apache/logging/log4j/util/LazyUtil.java    | 171 +++++++++++++++++++++
 .../org/apache/logging/log4j/util/LazyValue.java   |  85 ----------
 .../apache/logging/log4j/util/PropertiesUtil.java  |   8 +-
 .../apache/logging/log4j/util/ServiceRegistry.java |  12 +-
 .../logging/log4j/core/layout/GelfLayoutTest.java  |  19 ++-
 .../logging/log4j/core/layout/HtmlLayoutTest.java  |   4 +-
 .../log4j/core/layout/PatternLayoutTest.java       |   4 +-
 .../log4j/core/layout/Rfc5424LayoutTest.java       |  25 ++-
 .../log4j/core/layout/SyslogLayoutTest.java        |   4 +-
 .../log4j/core/config/AbstractConfiguration.java   |   4 +-
 .../core/config/DefaultConfigurationFactory.java   |  17 +-
 .../lookup/JmxRuntimeInputArgumentsLookup.java     |   7 +-
 .../core/net/ssl/SslConfigurationFactory.java      |   8 +-
 .../log4j/core/selector/BasicContextSelector.java  |  14 +-
 .../core/selector/ClassLoaderContextSelector.java  |   6 +-
 .../logging/log4j/core/time/ClockFactory.java      |   7 +-
 .../log4j/core/time/internal/CachedClock.java      |   7 +-
 .../core/time/internal/CoarseCachedClock.java      |   7 +-
 .../apache/logging/log4j/core/util/JsonUtils.java  |   8 +-
 .../log4j/csv/layout/CsvLogEventLayoutTest.java    |   4 +-
 .../log4j/jackson/json/layout/JsonLayoutTest.java  |   4 +-
 .../log4j/jackson/xml/layout/XmlLayoutTest.java    |   4 +-
 .../log4j/jackson/yaml/layout/YamlLayoutTest.java  |   4 +-
 .../layout/template/json/util/JsonWriter.java      |   9 +-
 .../logging/log4j/plugins/di/DefaultInjector.java  |   6 +-
 .../log4j/plugins/model/PluginRegistry.java        |   6 +-
 .../logging/log4j/plugins/model/PluginType.java    |  14 +-
 31 files changed, 352 insertions(+), 224 deletions(-)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
index f567d26938..6ba0391ddb 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -16,17 +16,6 @@
  */
 package org.apache.log4j.config;
 
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.log4j.ListAppender;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
@@ -38,10 +27,21 @@ import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 /**
  * Test configuration from XML.
  */
@@ -56,7 +56,7 @@ public class XmlConfigurationTest extends AbstractLog4j1ConfigurationTest {
         final ConfigurationSource source = new ConfigurationSource(inputStream);
         final LoggerContext context = LoggerContext.getContext(false);
         final Configuration configuration = context.getInjector()
-                .registerBinding(ConfigurationFactory.KEY, new LazyValue<>(XmlConfigurationFactory::new))
+                .registerBinding(ConfigurationFactory.KEY, Lazy.lazy(XmlConfigurationFactory::new))
                 .getInstance(ConfigurationFactory.KEY)
                 .getConfiguration(context, source);
         assertNotNull(configuration, "No configuration created");
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
index c2d6b46811..aaf1ba84c8 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
@@ -25,7 +25,7 @@ import org.apache.logging.log4j.spi.LoggerContextFactory;
 import org.apache.logging.log4j.spi.Provider;
 import org.apache.logging.log4j.spi.Terminable;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.ProviderUtil;
@@ -68,7 +68,7 @@ public class LogManager {
      * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
      * extended to allow multiple implementations to be used.
      */
-    private static final LazyValue<LoggerContextFactory> PROVIDER = LazyValue.from(() -> {
+    private static final Lazy<LoggerContextFactory> PROVIDER = Lazy.lazy(() -> {
         // Shortcut binding to force a specific logging implementation.
         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
         final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
index 07c35b2dbd..2bf80736a1 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
@@ -16,18 +16,17 @@
  */
 package org.apache.logging.log4j.message;
 
+import org.apache.logging.log4j.util.Lazy;
+import org.apache.logging.log4j.util.ServiceRegistry;
+import org.apache.logging.log4j.util.StringBuilderFormattable;
+import org.apache.logging.log4j.util.Strings;
+
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.function.Supplier;
-
-import org.apache.logging.log4j.util.LazyValue;
-import org.apache.logging.log4j.util.ServiceRegistry;
-import org.apache.logging.log4j.util.StringBuilderFormattable;
-import org.apache.logging.log4j.util.Strings;
 
 /**
  * Captures information about all running Threads.
@@ -35,7 +34,7 @@ import org.apache.logging.log4j.util.Strings;
 @AsynchronouslyFormattable
 public class ThreadDumpMessage implements Message, StringBuilderFormattable {
     private static final long serialVersionUID = -1103400781608841088L;
-    private static final Supplier<ThreadInfoFactory> FACTORY = new LazyValue<>(() -> {
+    private static final Lazy<ThreadInfoFactory> FACTORY = Lazy.lazy(() -> {
         final var services = ServiceRegistry.getInstance()
                 .getServices(ThreadInfoFactory.class, MethodHandles.lookup(), null);
         return services.isEmpty() ? new BasicThreadInfoFactory() : services.get(0);
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Lazy.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Lazy.java
new file mode 100644
index 0000000000..b91456d552
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Lazy.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.util;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Provides a lazily-initialized value from a {@code Supplier<T>}.
+ *
+ * @param <T> type of value
+ */
+public interface Lazy<T> extends Supplier<T> {
+    T value();
+
+    @Override
+    default T get() {
+        return value();
+    }
+
+    default <R> Lazy<R> map(final Function<? super T, ? extends R> function) {
+        return lazy(() -> function.apply(value()));
+    }
+
+    boolean isInitialized();
+
+    void set(final T newValue);
+
+    /**
+     * Creates a lazy value using the provided Supplier for initialization guarded by a Lock.
+     */
+    static <T> Lazy<T> lazy(final Supplier<T> supplier) {
+        return new LazyUtil.SafeLazy<>(supplier);
+    }
+
+    /**
+     * Creates a lazy value using the provided constant value.
+     */
+    static <T> Lazy<T> value(final T value) {
+        return new LazyUtil.Constant<>(value);
+    }
+
+    /**
+     * Creates a relaxed lazy value using the provided Supplier for initialization which may be invoked more than once
+     * in order to set the initialized value.
+     */
+    static <T> Lazy<T> relaxed(final Supplier<T> supplier) {
+        return new LazyUtil.ReleaseAcquireLazy<>(supplier);
+    }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyUtil.java
new file mode 100644
index 0000000000..7f15a0bb42
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyUtil.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.util;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Supplier;
+
+final class LazyUtil {
+    private static final Object NULL = new Object() {
+        @Override
+        public String toString() {
+            return "null";
+        }
+    };
+
+    static Object wrapNull(final Object value) {
+        return value == null ? NULL : value;
+    }
+
+    static <T> T unwrapNull(final Object value) {
+        return value == NULL ? null : cast(value);
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T> T cast(final Object o) {
+        return (T) o;
+    }
+
+    static class Constant<T> implements Lazy<T> {
+        private final T value;
+
+        Constant(final T value) {
+            this.value = value;
+        }
+
+        @Override
+        public T value() {
+            return value;
+        }
+
+        @Override
+        public boolean isInitialized() {
+            return true;
+        }
+
+        @Override
+        public void set(final T newValue) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String toString() {
+            return String.valueOf(value);
+        }
+    }
+
+    static class SafeLazy<T> implements Lazy<T> {
+        private final Lock lock = new ReentrantLock();
+        private final Supplier<T> supplier;
+        private volatile Object value;
+
+        SafeLazy(final Supplier<T> supplier) {
+            this.supplier = supplier;
+        }
+
+        @Override
+        public T value() {
+            Object value = this.value;
+            if (value == null) {
+                lock.lock();
+                try {
+                    value = this.value;
+                    if (value == null) {
+                        value = supplier.get();
+                        this.value = wrapNull(value);
+                    }
+                } finally {
+                    lock.unlock();
+                }
+            }
+            return unwrapNull(value);
+        }
+
+        @Override
+        public void set(final T newValue) {
+            value = newValue;
+        }
+
+        public void reset() {
+            value = null;
+        }
+
+        @Override
+        public boolean isInitialized() {
+            return value != null;
+        }
+
+        @Override
+        public String toString() {
+            return isInitialized() ? String.valueOf(value) : "Lazy value not initialized";
+        }
+    }
+
+    static class ReleaseAcquireLazy<T> implements Lazy<T> {
+        private static final VarHandle VALUE;
+        static {
+            try {
+                VALUE = MethodHandles.lookup()
+                        .findVarHandle(ReleaseAcquireLazy.class, "value", Object.class);
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        private final Supplier<T> supplier;
+        private volatile Object value;
+
+        ReleaseAcquireLazy(final Supplier<T> supplier) {
+            this.supplier = supplier;
+        }
+
+        @Override
+        public T value() {
+            final var currentValue = VALUE.getAcquire(this);
+            if (currentValue != null) {
+                return unwrapNull(currentValue);
+            }
+            final T newValue = supplier.get();
+            if (VALUE.compareAndSet(this, (Object) null, wrapNull(newValue))) {
+                return newValue;
+            }
+            final Object value = VALUE.getAcquire(this);
+            return unwrapNull(value);
+        }
+
+        @Override
+        public void set(final T newValue) {
+            // equivalent to VALUE.setVolatile(this, newValue)
+            value = newValue;
+        }
+
+        @Override
+        public boolean isInitialized() {
+            final var current = VALUE.getAcquire(this);
+            return current != null;
+        }
+
+        @Override
+        public String toString() {
+            return isInitialized() ? String.valueOf(VALUE.getOpaque(value)) : "Lazy value not initialized";
+        }
+    }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyValue.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyValue.java
deleted file mode 100644
index 20d7965e64..0000000000
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LazyValue.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.util;
-
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-/**
- * Provides a lazily-initialized value from a {@code Supplier<T>}.
- *
- * @param <T> type of value
- */
-public final class LazyValue<T> implements Supplier<T> {
-
-    private static final Object NULL_INSTANCE = new Object();
-
-    @SuppressWarnings("unchecked")
-    private static <T> T nullInstance() {
-        return (T) NULL_INSTANCE;
-    }
-
-    /**
-     * Creates a lazy value using the provided Supplier for initialization.
-     */
-    public static <T> LazyValue<T> from(final Supplier<T> supplier) {
-        return new LazyValue<>(supplier);
-    }
-
-    private final Supplier<T> supplier;
-    private volatile T value;
-
-    /**
-     * Constructs a lazy value using the provided Supplier for initialization.
-     *
-     * @param supplier value to lazily initialize
-     */
-    public LazyValue(final Supplier<T> supplier) {
-        this.supplier = supplier;
-    }
-
-    @Override
-    public T get() {
-        T value = this.value;
-        if (value == null) {
-            synchronized (this) {
-                value = this.value;
-                if (value == null) {
-                    value = supplier.get();
-                    this.value = value == null ? nullInstance() : value;
-                }
-            }
-        }
-        return value == NULL_INSTANCE ? null : value;
-    }
-
-    public void set(final T value) {
-        this.value = value;
-    }
-
-    /**
-     * Creates a LazyValue that maps the result of this LazyValue to another value.
-     *
-     * @param function mapping function to transform the result of this lazy value
-     * @param <R>      the return type of the new lazy value
-     * @return the new lazy value
-     */
-    public <R> LazyValue<R> map(final Function<? super T, ? extends R> function) {
-        return from(() -> function.apply(get()));
-    }
-}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index 699de74250..1f215de209 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -19,7 +19,6 @@ package org.apache.logging.log4j.util;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodHandles.Lookup;
 import java.nio.charset.Charset;
 import java.time.Duration;
 import java.time.temporal.ChronoUnit;
@@ -33,7 +32,6 @@ import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.ServiceLoader;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListSet;
 
@@ -54,8 +52,8 @@ public final class PropertiesUtil {
 
     private static final String LOG4J_PROPERTIES_FILE_NAME = "log4j2.component.properties";
     private static final String LOG4J_SYSTEM_PROPERTIES_FILE_NAME = "log4j2.system.properties";
-    private static final LazyValue<PropertiesUtil> COMPONENT_PROPERTIES =
-            LazyValue.from(() -> new PropertiesUtil(LOG4J_PROPERTIES_FILE_NAME));
+    private static final Lazy<PropertiesUtil> COMPONENT_PROPERTIES =
+            Lazy.lazy(() -> new PropertiesUtil(LOG4J_PROPERTIES_FILE_NAME));
 
     private final Environment environment;
 
@@ -121,7 +119,7 @@ public final class PropertiesUtil {
      * @return the main Log4j PropertiesUtil instance.
      */
     public static PropertiesUtil getProperties() {
-        return COMPONENT_PROPERTIES.get();
+        return COMPONENT_PROPERTIES.value();
     }
 
     /**
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
index 23f55b6b16..f52859a5db 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
@@ -24,10 +24,11 @@ import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static org.apache.logging.log4j.util.LazyUtil.cast;
+
 /**
  * Registry for service instances loaded from {@link ServiceLoader}. This abstracts the differences between using a flat
  * classpath, a module path, and OSGi modules.
@@ -35,7 +36,7 @@ import java.util.stream.Stream;
  * @since 3.0.0
  */
 public class ServiceRegistry {
-    private static final Supplier<ServiceRegistry> INSTANCE = new LazyValue<>(ServiceRegistry::new);
+    private static final Lazy<ServiceRegistry> INSTANCE = Lazy.relaxed(ServiceRegistry::new);
 
     /**
      * Returns the singleton ServiceRegistry instance.
@@ -44,11 +45,6 @@ public class ServiceRegistry {
         return INSTANCE.get();
     }
 
-    @SuppressWarnings("unchecked")
-    private static <T> T cast(final Object o) {
-        return (T) o;
-    }
-
     private final Map<Class<?>, List<?>> mainServices = new ConcurrentHashMap<>();
     private final Map<Long, Map<Class<?>, List<?>>> bundleServices = new ConcurrentHashMap<>();
 
@@ -61,7 +57,7 @@ public class ServiceRegistry {
      * load services.
      *
      * @param serviceType         service class
-     * @param loaderCallerContext function located in same module as caller context
+     * @param lookup              MethodHandle lookup created in same module as caller context
      *                            to use for loading services
      * @param validator           if non-null, used to validate service instances,
      *                            removing invalid instances from the returned list
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
index 5bbb540d2f..28941a49b8 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
@@ -16,13 +16,7 @@
  */
 package org.apache.logging.log4j.core.layout;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.InflaterInputStream;
-
+import com.fasterxml.jackson.core.io.JsonStringEncoder;
 import org.apache.commons.io.IOUtils;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.ThreadContext;
@@ -40,12 +34,17 @@ import org.apache.logging.log4j.core.util.KeyValuePair;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
 import org.apache.logging.log4j.util.Chars;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
-import com.fasterxml.jackson.core.io.JsonStringEncoder;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
 
 import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -74,7 +73,7 @@ public class GelfLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new)::value);
         ctx.reconfigure();
     }
 
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
index 1ae2921f9a..266ca513d0 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java
@@ -32,7 +32,7 @@ import org.apache.logging.log4j.core.time.internal.format.FixedDateFormat;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -87,7 +87,7 @@ public class HtmlLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new)::value);
         ctx.reconfigure();
     }
 
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java
index 6346009902..f5211d307f 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java
@@ -29,7 +29,7 @@ import org.apache.logging.log4j.core.lookup.MainMapLookup;
 import org.apache.logging.log4j.core.test.BasicConfigurationFactory;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -52,7 +52,7 @@ public class PatternLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new));
         ctx.reconfigure();
     }
 
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
index 60a6343a14..80bc899987 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
@@ -16,12 +16,6 @@
  */
 package org.apache.logging.log4j.core.layout;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Function;
-
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext;
@@ -47,7 +41,7 @@ import org.apache.logging.log4j.plugins.model.PluginNamespace;
 import org.apache.logging.log4j.plugins.model.PluginType;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
@@ -55,13 +49,14 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.assertj.core.api.Assertions.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
 
 @UsingAnyThreadContext
 public class Rfc5424LayoutTest {
@@ -92,7 +87,7 @@ public class Rfc5424LayoutTest {
     public static void setupClass() {
         StatusLogger.getLogger().setLevel(Level.OFF);
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new));
         ctx.reconfigure();
     }
 
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SyslogLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SyslogLayoutTest.java
index 09684b70db..bacc71a710 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SyslogLayoutTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SyslogLayoutTest.java
@@ -28,7 +28,7 @@ import org.apache.logging.log4j.core.test.BasicConfigurationFactory;
 import org.apache.logging.log4j.core.test.appender.ListAppender;
 import org.apache.logging.log4j.message.StructuredDataMessage;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -55,7 +55,7 @@ public class SyslogLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new));
         ctx.reconfigure();
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 3521a4abd6..64a4511167 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -60,7 +60,7 @@ import org.apache.logging.log4j.plugins.di.Keys;
 import org.apache.logging.log4j.plugins.model.PluginNamespace;
 import org.apache.logging.log4j.plugins.model.PluginType;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.ServiceRegistry;
@@ -308,7 +308,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         if (configSource.getLastModified() > 0) {
             final Source cfgSource = new Source(configSource);
             final Key<WatcherFactory> key = Key.forClass(WatcherFactory.class);
-            injector.registerBindingIfAbsent(key, LazyValue.from(() ->
+            injector.registerBindingIfAbsent(key, Lazy.lazy(() ->
                     new WatcherFactory(injector.getInstance(Watcher.PLUGIN_CATEGORY_KEY))));
             final Watcher watcher = injector.getInstance(key)
                     .newWatcher(cfgSource, this, reconfigurable, listeners, configSource.getLastModified());
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java
index bdcad7fb94..531cfd63e6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java
@@ -24,7 +24,7 @@ import org.apache.logging.log4j.core.util.Loader;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.plugins.Inject;
 import org.apache.logging.log4j.plugins.di.Injector;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.Strings;
@@ -36,7 +36,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * Default factory for using a plugin selected based on the configuration source.
@@ -46,11 +45,11 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
     private static final String ALL_TYPES = "*";
     private static final String OVERRIDE_PARAM = "override";
 
-    private final Supplier<List<ConfigurationFactory>> configurationFactories;
+    private final Lazy<List<ConfigurationFactory>> configurationFactories;
 
     @Inject
     public DefaultConfigurationFactory(final Injector injector) {
-        configurationFactories = new LazyValue<>(() -> loadConfigurationFactories(injector));
+        configurationFactories = Lazy.lazy(() -> loadConfigurationFactories(injector));
     }
 
     /**
@@ -98,7 +97,7 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
                     return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr);
                 }
             }
-            for (final ConfigurationFactory factory : configurationFactories.get()) {
+            for (final ConfigurationFactory factory : configurationFactories.value()) {
                 final String[] types = factory.getSupportedTypes();
                 if (types != null) {
                     for (final String type : types) {
@@ -128,7 +127,7 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
                 return new CompositeConfiguration(configs);
             }
             final String configLocationStr = configLocation.toString();
-            for (final ConfigurationFactory factory : configurationFactories.get()) {
+            for (final ConfigurationFactory factory : configurationFactories.value()) {
                 final String[] types = factory.getSupportedTypes();
                 if (types != null) {
                     for (final String type : types) {
@@ -180,7 +179,7 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
             LOGGER.catching(Level.DEBUG, ex);
         }
         if (source != null) {
-            for (final ConfigurationFactory factory : configurationFactories.get()) {
+            for (final ConfigurationFactory factory : configurationFactories.value()) {
                 if (requiredVersion != null && !factory.getVersion().equals(requiredVersion)) {
                     continue;
                 }
@@ -203,7 +202,7 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
     private Configuration getConfiguration(final LoggerContext loggerContext, final boolean isTest, final String name) {
         final boolean named = Strings.isNotEmpty(name);
         final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
-        for (final ConfigurationFactory factory : configurationFactories.get()) {
+        for (final ConfigurationFactory factory : configurationFactories.value()) {
             String configName;
             final String prefix = isTest ? factory.getTestPrefix() : factory.getDefaultPrefix();
             final String[] types = factory.getSupportedTypes();
@@ -239,7 +238,7 @@ public class DefaultConfigurationFactory extends ConfigurationFactory {
     public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
         if (source != null) {
             final String config = source.getLocation();
-            for (final ConfigurationFactory factory : configurationFactories.get()) {
+            for (final ConfigurationFactory factory : configurationFactories.value()) {
                 final String[] types = factory.getSupportedTypes();
                 if (types != null) {
                     for (final String type : types) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java
index 5772b9133c..083b355069 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java
@@ -19,12 +19,11 @@ package org.apache.logging.log4j.core.lookup;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.plugins.PluginFactory;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 
 import java.lang.management.ManagementFactory;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Supplier;
 
 /**
  * Maps JVM input arguments (but not main arguments) using JMX to acquire JVM arguments.
@@ -36,14 +35,14 @@ import java.util.function.Supplier;
 @Plugin("jvmrunargs")
 public class JmxRuntimeInputArgumentsLookup extends MapLookup {
 
-    private static final Supplier<JmxRuntimeInputArgumentsLookup> INSTANCE = LazyValue.from(() -> {
+    private static final Lazy<JmxRuntimeInputArgumentsLookup> INSTANCE = Lazy.lazy(() -> {
         final List<String> argsList = ManagementFactory.getRuntimeMXBean().getInputArguments();
         return new JmxRuntimeInputArgumentsLookup(MapLookup.toMap(argsList));
     });
 
     @PluginFactory
     public static JmxRuntimeInputArgumentsLookup getInstance() {
-        return INSTANCE.get();
+        return INSTANCE.value();
     }
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java
index 7c1bf3b583..549d2cfc81 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java
@@ -18,11 +18,9 @@ package org.apache.logging.log4j.core.net.ssl;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.PropertiesUtil;
 
-import java.util.function.Supplier;
-
 /**
  * Creates an SSL configuration from Log4j properties.
  */
@@ -44,7 +42,7 @@ public class SslConfigurationFactory {
     private static final String keyStoreKeyManagerFactoryAlgorithm = "log4j2.keyStore.keyManagerFactoryAlgorithm";
     private static final String verifyHostName = "log4j2.ssl.verifyHostName";
 
-    private static final Supplier<SslConfiguration> SSL_CONFIGURATION = LazyValue.from(() -> {
+    private static final Lazy<SslConfiguration> SSL_CONFIGURATION = Lazy.lazy(() -> {
         final PropertiesUtil props = PropertiesUtil.getProperties();
         return createSslConfiguration(props);
     });
@@ -93,6 +91,6 @@ public class SslConfigurationFactory {
     }
 
     public static SslConfiguration getSslConfiguration() {
-        return SSL_CONFIGURATION.get();
+        return SSL_CONFIGURATION.value();
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
index 77fbfb173a..e814928a27 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
@@ -16,10 +16,6 @@
  */
 package org.apache.logging.log4j.core.selector;
 
-import java.net.URI;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.impl.ContextAnchor;
@@ -28,7 +24,11 @@ import org.apache.logging.log4j.plugins.Singleton;
 import org.apache.logging.log4j.plugins.di.Injector;
 import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Returns either this Thread's context or the default LoggerContext.
@@ -38,7 +38,7 @@ public class BasicContextSelector implements ContextSelector, LoggerContextShutd
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
-    protected final LazyValue<LoggerContext> context = LazyValue.from(this::createContext);
+    protected final Lazy<LoggerContext> context = Lazy.lazy(this::createContext);
     protected final Injector injector;
 
     @Inject
@@ -108,7 +108,7 @@ public class BasicContextSelector implements ContextSelector, LoggerContextShutd
 
     @Override
     public List<LoggerContext> getLoggerContexts() {
-        return List.of(context.get());
+        return List.of(context.value());
     }
 
     protected LoggerContext createContext() {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index 372a6103ee..c912000892 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -23,7 +23,7 @@ import org.apache.logging.log4j.plugins.Singleton;
 import org.apache.logging.log4j.plugins.di.Injector;
 import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.StackLocatorUtil;
 
 import java.lang.ref.WeakReference;
@@ -53,7 +53,7 @@ public class ClassLoaderContextSelector implements ContextSelector, LoggerContex
 
     protected static final StatusLogger LOGGER = StatusLogger.getLogger();
 
-    protected final LazyValue<LoggerContext> defaultContext = LazyValue.from(() -> createContext(defaultContextName(), null));
+    protected final Lazy<LoggerContext> defaultContext = Lazy.lazy(() -> createContext(defaultContextName(), null));
     protected final Map<String, AtomicReference<WeakReference<LoggerContext>>> contextMap = new ConcurrentHashMap<>();
 
     protected final Injector injector;
@@ -291,7 +291,7 @@ public class ClassLoaderContextSelector implements ContextSelector, LoggerContex
     }
 
     protected LoggerContext getDefault() {
-        return defaultContext.get();
+        return defaultContext.value();
     }
 
     protected String defaultContextName() {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/ClockFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/ClockFactory.java
index 6dbf6835ae..185f27e4e2 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/ClockFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/ClockFactory.java
@@ -21,7 +21,7 @@ import org.apache.logging.log4j.core.time.internal.CoarseCachedClock;
 import org.apache.logging.log4j.core.time.internal.SystemClock;
 import org.apache.logging.log4j.plugins.di.DI;
 import org.apache.logging.log4j.plugins.di.Injector;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 
 /**
  * Factory for {@code Clock} objects.
@@ -35,7 +35,8 @@ public final class ClockFactory {
      * implementation class. The value of this property is {@value}.
      */
     public static final String PROPERTY_NAME = "log4j.Clock";
-    private static final LazyValue<Clock> FALLBACK = new LazyValue<>(() -> {
+    private static final Lazy<Clock> FALLBACK = Lazy.lazy(() -> {
+        // TODO(ms): split out clock bindings for smaller fallback init
         final Injector injector = DI.createInjector();
         injector.init();
         return injector.getInstance(Clock.KEY);
@@ -65,7 +66,7 @@ public final class ClockFactory {
      */
     @Deprecated
     public static Clock getClock() {
-        return FALLBACK.get();
+        return FALLBACK.value();
     }
 
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CachedClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CachedClock.java
index e151dcc458..312ec10f98 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CachedClock.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CachedClock.java
@@ -19,10 +19,9 @@ package org.apache.logging.log4j.core.time.internal;
 import org.apache.logging.log4j.core.time.Clock;
 import org.apache.logging.log4j.core.util.Log4jThread;
 import org.apache.logging.log4j.plugins.Factory;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 
 import java.util.concurrent.locks.LockSupport;
-import java.util.function.Supplier;
 
 /**
  * Implementation of the {@code Clock} interface that tracks the time in a
@@ -34,7 +33,7 @@ import java.util.function.Supplier;
  */
 public final class CachedClock implements Clock {
     private static final int UPDATE_THRESHOLD = 1000;
-    private static final Supplier<CachedClock> INSTANCE = new LazyValue<>(CachedClock::new);
+    private static final Lazy<CachedClock> INSTANCE = Lazy.lazy(CachedClock::new);
     private volatile long millis = System.currentTimeMillis();
     private short count = 0;
 
@@ -54,7 +53,7 @@ public final class CachedClock implements Clock {
     @Factory
     public static CachedClock instance() {
         // LOG4J2-819: use lazy initialization of threads
-        return INSTANCE.get();
+        return INSTANCE.value();
     }
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CoarseCachedClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CoarseCachedClock.java
index 18b9a3e8e2..e8bb95d6b0 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CoarseCachedClock.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/time/internal/CoarseCachedClock.java
@@ -18,17 +18,16 @@ package org.apache.logging.log4j.core.time.internal;
 
 import org.apache.logging.log4j.core.time.Clock;
 import org.apache.logging.log4j.core.util.Log4jThread;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 
 import java.util.concurrent.locks.LockSupport;
-import java.util.function.Supplier;
 
 /**
  * This Clock implementation is similar to CachedClock. It is slightly faster at
  * the cost of some accuracy.
  */
 public final class CoarseCachedClock implements Clock {
-    private static final Supplier<CoarseCachedClock> INSTANCE = new LazyValue<>(CoarseCachedClock::new);
+    private static final Lazy<CoarseCachedClock> INSTANCE = Lazy.lazy(CoarseCachedClock::new);
     // ignore IDE complaints; volatile long is fine
     private volatile long millis = System.currentTimeMillis();
 
@@ -55,7 +54,7 @@ public final class CoarseCachedClock implements Clock {
      */
     public static CoarseCachedClock instance() {
         // LOG4J2-819: use lazy initialization of threads
-        return INSTANCE.get();
+        return INSTANCE.value();
     }
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java
index 92957ccd69..3815967aeb 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java
@@ -16,9 +16,7 @@
  */
 package org.apache.logging.log4j.core.util;
 
-import org.apache.logging.log4j.util.LazyValue;
-
-import java.util.function.Supplier;
+import org.apache.logging.log4j.util.Lazy;
 
 /**
  * This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
@@ -33,7 +31,7 @@ public final class JsonUtils {
      * to use after backslash; and negative values that generic (backslash - u)
      * escaping is to be used.
      */
-    private static final Supplier<int[]> ESC_CODES = LazyValue.from(() -> {
+    private static final Lazy<int[]> ESC_CODES = Lazy.relaxed(() -> {
         final int[] table = new int[128];
         // Control chars need generic escape sequence
         for (int i = 0; i < 32; ++i) {
@@ -77,7 +75,7 @@ public final class JsonUtils {
      */
     public static void quoteAsString(final CharSequence input, final StringBuilder output) {
         final char[] qbuf = getQBuf();
-        final int[] escCodes = ESC_CODES.get();
+        final int[] escCodes = ESC_CODES.value();
         final int escCodeCount = escCodes.length;
         int inPtr = 0;
         final int inputLen = input.length();
diff --git a/log4j-csv/src/test/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayoutTest.java b/log4j-csv/src/test/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayoutTest.java
index 305c4efeb0..ab63cc6e3a 100644
--- a/log4j-csv/src/test/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayoutTest.java
+++ b/log4j-csv/src/test/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayoutTest.java
@@ -25,7 +25,7 @@ import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.test.BasicConfigurationFactory;
 import org.apache.logging.log4j.core.test.appender.ListAppender;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -52,7 +52,7 @@ public class CsvLogEventLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new)::value);
         ctx.reconfigure();
     }
 
diff --git a/log4j-layout-jackson-json/src/test/java/org/apache/logging/log4j/jackson/json/layout/JsonLayoutTest.java b/log4j-layout-jackson-json/src/test/java/org/apache/logging/log4j/jackson/json/layout/JsonLayoutTest.java
index 83dba6e2c4..af005df15f 100644
--- a/log4j-layout-jackson-json/src/test/java/org/apache/logging/log4j/jackson/json/layout/JsonLayoutTest.java
+++ b/log4j-layout-jackson-json/src/test/java/org/apache/logging/log4j/jackson/json/layout/JsonLayoutTest.java
@@ -45,7 +45,7 @@ import org.apache.logging.log4j.message.ReusableMessageFactory;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.SortedArrayStringMap;
 import org.apache.logging.log4j.util.StringMap;
 import org.apache.logging.log4j.util.Strings;
@@ -89,7 +89,7 @@ public class JsonLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new)::value);
         ctx.reconfigure();
     }
 
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
index 6b29fec8b6..96b1705cf9 100644
--- a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
@@ -41,7 +41,7 @@ import org.apache.logging.log4j.jackson.xml.Log4jXmlObjectMapper;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
@@ -69,7 +69,7 @@ public class XmlLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new));
         ctx.reconfigure();
     }
 
diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java
index e23db19362..1f16b0b90a 100644
--- a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java
+++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java
@@ -35,7 +35,7 @@ import org.apache.logging.log4j.jackson.yaml.Log4jYamlObjectMapper;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
@@ -63,7 +63,7 @@ public class YamlLayoutTest {
     @BeforeAll
     public static void setupClass() {
         final LoggerContext ctx = LoggerContext.getContext();
-        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, new LazyValue<>(BasicConfigurationFactory::new));
+        ctx.getInjector().registerBinding(ConfigurationFactory.KEY, Lazy.lazy(BasicConfigurationFactory::new));
         ctx.reconfigure();
     }
 
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/JsonWriter.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/JsonWriter.java
index ce136edf44..480647de8c 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/JsonWriter.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/JsonWriter.java
@@ -17,7 +17,7 @@
 package org.apache.logging.log4j.layout.template.json.util;
 
 import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.StringBuilderFormattable;
 import org.apache.logging.log4j.util.StringMap;
 
@@ -28,7 +28,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.function.BiConsumer;
-import java.util.function.Supplier;
 
 /**
  * A simple JSON writer with support for common Java data types.
@@ -68,7 +67,7 @@ public final class JsonWriter implements AutoCloseable, Cloneable {
      * character to use after backslash; and negative values, that generic
      * (backslash - u) escaping is to be used.
      */
-    private static final Supplier<int[]> ESC_CODES = LazyValue.from(() -> {
+    private static final Lazy<int[]> ESC_CODES = Lazy.relaxed(() -> {
         final int[] table = new int[128];
         // Control chars need generic escape sequence
         for (int i = 0; i < 32; ++i) {
@@ -600,7 +599,7 @@ public final class JsonWriter implements AutoCloseable, Cloneable {
                         : 0;
         final int limit = offset + length + surrogateCorrection;
         int i = offset;
-        final int[] escCodes = ESC_CODES.get();
+        final int[] escCodes = ESC_CODES.value();
         outer:
         while (i < limit) {
             while (true) {
@@ -678,7 +677,7 @@ public final class JsonWriter implements AutoCloseable, Cloneable {
                         : 0;
         final int limit = offset + length + surrogateCorrection;
         int i = offset;
-        final int[] escCodes = ESC_CODES.get();
+        final int[] escCodes = ESC_CODES.value();
         outer:
         while (i < limit) {
             while (true) {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
index c61f10df99..6d1d28d8f9 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
@@ -40,7 +40,7 @@ import org.apache.logging.log4j.plugins.validation.ConstraintValidator;
 import org.apache.logging.log4j.plugins.visit.NodeVisitor;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.EnglishEnums;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.ServiceRegistry;
 import org.apache.logging.log4j.util.StringBuilders;
 
@@ -305,7 +305,7 @@ class DefaultInjector implements Injector {
     }
 
     private Supplier<PluginNamespace> createPluginNamespaceFactory(final Key<PluginNamespace> key) {
-        return LazyValue.from(() -> getInstance(PluginRegistry.class).getNamespace(key.getNamespace(), getPluginPackages()));
+        return Lazy.lazy(() -> getInstance(PluginRegistry.class).getNamespace(key.getNamespace(), getPluginPackages()))::value;
     }
 
     private List<String> getPluginPackages() {
@@ -829,7 +829,7 @@ class DefaultInjector implements Injector {
 
         @Override
         public <T> Supplier<T> get(final Key<T> key, final Supplier<T> unscoped) {
-            return TypeUtil.cast(singletonProviders.computeIfAbsent(key, ignored -> new LazyValue<>(unscoped)));
+            return TypeUtil.cast(singletonProviders.computeIfAbsent(key, ignored -> Lazy.lazy(unscoped)::value));
         }
 
         @Override
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginRegistry.java
index f77e82d1d7..0d4a4bbb00 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginRegistry.java
@@ -25,7 +25,7 @@ import org.apache.logging.log4j.plugins.Singleton;
 import org.apache.logging.log4j.plugins.di.Keys;
 import org.apache.logging.log4j.plugins.util.ResolverUtil;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.Lazy;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.Strings;
 
@@ -61,7 +61,7 @@ public class PluginRegistry {
     /**
      * Contains plugins found from {@link PluginService} services and legacy Log4j2Plugins.dat cache files in the main CLASSPATH.
      */
-    private final LazyValue<Namespaces> mainPluginNamespaces = LazyValue.from(() -> {
+    private final Lazy<Namespaces> mainPluginNamespaces = Lazy.lazy(() -> {
         final Namespaces namespaces = decodeCacheFiles(LoaderUtil.getClassLoader());
         Throwable throwable = null;
         ClassLoader errorClassLoader = null;
@@ -253,7 +253,7 @@ public class PluginRegistry {
         final var pluginNamespace = new PluginNamespace(namespace);
 
         // First, iterate the PluginService services and legacy Log4j2Plugin.dat files found in the main CLASSPATH
-        final Namespaces builtInPlugins = mainPluginNamespaces.get();
+        final Namespaces builtInPlugins = mainPluginNamespaces.value();
         if (builtInPlugins != null) {
             pluginNamespace.mergeAll(builtInPlugins.get(namespace));
         }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginType.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginType.java
index 028d56ee77..c210d10713 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginType.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginType.java
@@ -17,9 +17,7 @@
 package org.apache.logging.log4j.plugins.model;
 
 import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.apache.logging.log4j.util.LazyValue;
-
-import java.util.function.Supplier;
+import org.apache.logging.log4j.util.Lazy;
 
 /**
  * Plugin Descriptor. This is a memento object for Plugin annotations paired to their annotated classes.
@@ -30,7 +28,7 @@ import java.util.function.Supplier;
 public class PluginType<T> {
 
     private final PluginEntry pluginEntry;
-    private final Supplier<Class<T>> pluginClass;
+    private final Lazy<Class<T>> pluginClass;
 
     /**
      * Constructor.
@@ -41,7 +39,7 @@ public class PluginType<T> {
     public PluginType(
             final PluginEntry pluginEntry, final Class<T> pluginClass) {
         this.pluginEntry = pluginEntry;
-        this.pluginClass = () -> pluginClass;
+        this.pluginClass = Lazy.value(pluginClass);
     }
 
     /**
@@ -51,7 +49,7 @@ public class PluginType<T> {
      */
     public PluginType(final PluginEntry pluginEntry, final ClassLoader classLoader) {
         this.pluginEntry = pluginEntry;
-        this.pluginClass = LazyValue.from(() -> {
+        this.pluginClass = Lazy.lazy(() -> {
             try {
                 return TypeUtil.cast(classLoader.loadClass(pluginEntry.getClassName()));
             } catch (final ClassNotFoundException e) {
@@ -66,7 +64,7 @@ public class PluginType<T> {
     }
 
     public Class<T> getPluginClass() {
-        return pluginClass.get();
+        return pluginClass.value();
     }
 
     public String getElementType() {
@@ -105,7 +103,7 @@ public class PluginType<T> {
 
     @Override
     public String toString() {
-        return "PluginType [pluginClass=" + pluginClass.get() +
+        return "PluginType [pluginClass=" + pluginClass.toString() +
                 ", key=" + pluginEntry.getKey() +
                 ", elementType=" + pluginEntry.getElementType() +
                 ", isObjectPrintable=" + pluginEntry.isPrintable() +