You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单例线程安全块多线程问题:并行读写时如何获取实时更新的密码?

如何确保多线程下读取配置总能获取最新值?

问题场景:当两个线程并行操作时,一个线程更新密码配置,另一个线程同时读取,怎样保证读取操作总能拿到最新的密码值?

代码示例

package com.designpattern.singleton;

public class Singleton {
    public static void main(String args[]) {
        Thread threadblock1 = new Thread(new ThreadSafeBlock1());
        threadblock1.start();

        Thread threadblock2 = new Thread(new ThreadSafeBlock2());
        threadblock2.start();
    }

    static class ThreadSafeBlock1 implements Runnable {
        @Override
        public void run() {
            ConfigManagerWithThreadSafeBlock safeblockinit1 = ConfigManagerWithThreadSafeBlock.getInstance();
            System.out.println("Threadsafe Block1");
            safeblockinit1.update("password", "newpassword");
        }
    }

    static class ThreadSafeBlock2 implements Runnable {
        @Override
        public void run() {
            ConfigManagerWithThreadSafeBlock safeblockinit2 = ConfigManagerWithThreadSafeBlock.getInstance();
            safeblockinit2.display();
        }
    }
}

package com.designpattern.singleton;

import java.util.HashMap;
import java.util.Map;

public class ConfigManagerWithThreadSafeBlock {
    
    private static ConfigManagerWithThreadSafeBlock threadsafeblock;

    private Map<String, String> configMap = new HashMap<>() {{
            put("password", "oldpassword");
    }};
  
    private ConfigManagerWithThreadSafeBlock() {
  
    }
  
    public void update(String key, String value) {
        configMap.put(key, value);
    }
    
    public void display() {
        for (Map.Entry<String, String> entry : configMap.entrySet()) {
            System.out.println(entry.getKey()+" : "+entry.getValue());
        }
    }
  
    public static ConfigManagerWithThreadSafeBlock getInstance() {
        ConfigManagerWithThreadSafeBlock result = threadsafeblock;
        if (result != null) {
            return result;
        }
        synchronized(ConfigManagerWithThreadSafeBlock.class) {
            if (threadsafeblock == null) {
                threadsafeblock = new ConfigManagerWithThreadSafeBlock();
            }
            return threadsafeblock;
        }
    }
}

实际输出

Threadsafe Block1
Threadsafe Block2
password : oldpassword

期望输出

Threadsafe Block1
Threadsafe Block2
password : newpassword

问题原因及解决方案

当前代码存在两个核心问题:

  1. HashMap非线程安全:HashMap的put和遍历操作没有同步机制,多线程下可能出现数据不一致,甚至读取到脏数据。
  2. 缺乏内存可见性保障:一个线程更新的配置值,其他线程可能因为CPU缓存的原因无法立即看到最新值。

针对这些问题,有几种可行的解决方式:

方式一:使用线程安全的ConcurrentHashMap替换HashMap

将configMap的类型改为ConcurrentHashMap,它本身就提供了线程安全的读写操作,同时保证了内存可见性:

private Map<String, String> configMap = new ConcurrentHashMap<>() {{
        put("password", "oldpassword");
}};

方式二:为读写方法添加同步锁

给update和display方法加上synchronized关键字,保证同一时间只有一个线程能执行这些方法,既保证原子性也保证可见性:

public synchronized void update(String key, String value) {
    configMap.put(key, value);
}

public synchronized void display() {
    for (Map.Entry<String, String> entry : configMap.entrySet()) {
        System.out.println(entry.getKey()+" : "+entry.getValue());
    }
}

方式三:结合volatile和同步操作

给configMap加上volatile关键字保证可见性,同时对put和遍历操作加锁(因为HashMap的put不是原子操作):

private volatile Map<String, String> configMap = new HashMap<>() {{
        put("password", "oldpassword");
}};

public void update(String key, String value) {
    synchronized(this) {
        configMap.put(key, value);
    }
}

public void display() {
    synchronized(this) {
        for (Map.Entry<String, String> entry : configMap.entrySet()) {
            System.out.println(entry.getKey()+" : "+entry.getValue());
        }
    }
}

另外补充:原单例的双重检查锁定中,threadsafeblock变量应该加上volatile关键字,防止指令重排导致的半初始化对象问题:

private static volatile ConfigManagerWithThreadSafeBlock threadsafeblock;

内容的提问来源于stack exchange,提问作者Rathina Vel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:41:00