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

lizhimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-clients.git


The following commit(s) were added to refs/heads/master by this push:
     new fd58daba [ISSUE #1145] Make ClientServiceProvider.loadService 
Thread-Safe with Lazy Initialization (#1146)
fd58daba is described below

commit fd58dabaa43c4218344b87f6bbcf1251bfb9b5b8
Author: qianye <[email protected]>
AuthorDate: Wed Dec 10 13:58:18 2025 +0800

    [ISSUE #1145] Make ClientServiceProvider.loadService Thread-Safe with Lazy 
Initialization (#1146)
---
 .../client/apis/ClientServiceProvider.java         | 40 ++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git 
a/java/client-apis/src/main/java/org/apache/rocketmq/client/apis/ClientServiceProvider.java
 
b/java/client-apis/src/main/java/org/apache/rocketmq/client/apis/ClientServiceProvider.java
index 723ca2e2..39df3a5c 100644
--- 
a/java/client-apis/src/main/java/org/apache/rocketmq/client/apis/ClientServiceProvider.java
+++ 
b/java/client-apis/src/main/java/org/apache/rocketmq/client/apis/ClientServiceProvider.java
@@ -30,7 +30,47 @@ import 
org.apache.rocketmq.client.apis.producer.ProducerBuilder;
  * <a href="https://en.wikipedia.org/wiki/Service_provider_interface";>Java SPI 
mechanism</a>.
  */
 public interface ClientServiceProvider {
+
+    /**
+     * To avoid potential concurrency issues, the {@link #loadService()} logic
+     * has been changed to use lazy initialization with caching:
+     * <p>
+     * 1. Lazy loading + caching:
+     * - On the first call, the implementation is loaded via {@link 
ServiceLoader}
+     * and cached in {@link Holder#INSTANCE};
+     * - Subsequent calls simply return the cached instance.
+     * <p>
+     * 2. If you need the old behavior (i.e., always load through ServiceLoader
+     * each time), you can call {@link #doLoad()} directly:
+     * - {@link #doLoad()} does not cache anything; it creates a new 
ServiceLoader
+     * and loads an implementation on every call;
+     * - You are responsible for handling any concurrency control when using
+     * {@link #doLoad()} directly.
+     */
+
+    class Holder {
+        static volatile ClientServiceProvider INSTANCE;
+
+        private Holder() {
+            // prevents instantiation
+        }
+    }
+
     static ClientServiceProvider loadService() {
+        ClientServiceProvider inst = Holder.INSTANCE;
+        if (inst != null) {
+            return inst;
+        }
+        synchronized (ClientServiceProvider.class) {
+            if (Holder.INSTANCE != null) {
+                return Holder.INSTANCE;
+            }
+            Holder.INSTANCE = doLoad();
+            return Holder.INSTANCE;
+        }
+    }
+
+    static ClientServiceProvider doLoad() {
         final ServiceLoader<ClientServiceProvider> loaders = 
ServiceLoader.load(ClientServiceProvider.class);
         final Iterator<ClientServiceProvider> iterators = loaders.iterator();
         if (iterators.hasNext()) {

Reply via email to