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

Java同步块动态锁监控问题:同哈希线程等待异哈希线程放行

问题分析与解决方案

看起来你踩了一个经典的锁对象不唯一的坑!问题根源有两个:

  • 你用synchronized(campaignEnity.getHash()),但getHash()每次返回新的String对象——虽然字符串内容相同,但对象本身是独立的,所以每个线程其实都在锁自己的专属对象,完全起不到同哈希值线程间的同步作用。
  • 你尝试用静态Map存锁,但大概率是没用线程安全的Map实现,或者没处理并发创建锁的原子性逻辑,导致相同哈希值的线程拿到了不同的锁对象,自然没法互相等待。

可靠解决方案:用ConcurrentHashMap管理锁对象

我们可以用ConcurrentHashMap(线程安全的哈希表)来存储哈希值对应的锁对象,通过它的computeIfAbsent方法原子性地获取或创建锁,保证相同哈希值的线程一定会拿到同一个锁对象。

代码示例

import java.util.concurrent.ConcurrentHashMap;

public class YourCampaignProcessor {
    // 静态线程安全Map,存储哈希值 -> 对应锁对象
    private static final ConcurrentHashMap<String, Object> HASH_LOCK_MAP = new ConcurrentHashMap<>();

    public CampaignProcessorEntity processCampaign(CampaignProcessorEntity campaignEntity) {
        String targetHash = campaignEntity.getHash();
        
        // 原子操作:如果哈希值对应的锁不存在,就新建一个Object作为锁;否则直接返回已有的锁
        Object lock = HASH_LOCK_MAP.computeIfAbsent(targetHash, k -> new Object());

        synchronized (lock) {
            try {
                // 这里放你需要运行数分钟的业务代码
                // ...
            } finally {
                // 【可选】如果该哈希值不会再被后续线程使用,可以移除锁对象避免内存泄漏
                // 注意:如果哈希值会重复出现,不要移除!否则新线程会创建新锁,和旧线程的锁不同步
                // HASH_LOCK_MAP.remove(targetHash);
            }
        }

        return campaignEntity;
    }
}

为什么这个方案能解决问题?

  • ConcurrentHashMap本身是线程安全的,computeIfAbsent方法是原子执行的——多个线程同时请求同一个哈希值的锁时,只会有一个线程创建锁对象,其他线程直接复用已创建的锁,完美保证锁的唯一性。
  • 我们用独立的Object作为锁对象,避开了String常量池可能带来的意外锁共享问题(比如其他地方也用相同内容的String当锁,会导致不必要的同步)。

额外注意事项:内存泄漏问题

如果你的哈希值数量非常多,而且很多哈希值只会被使用一次,HASH_LOCK_MAP会一直持有锁对象导致内存占用持续增加。这种情况可以考虑用带过期策略的缓存来管理锁,比如Guava的LoadingCache:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.TimeUnit;

public class YourCampaignProcessor {
    // 缓存锁对象,10分钟未访问自动过期移除
    private static final LoadingCache<String, Object> LOCK_CACHE = CacheBuilder.newBuilder()
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, Object>() {
                @Override
                public Object load(String hash) {
                    return new Object();
                }
            });

    public CampaignProcessorEntity processCampaign(CampaignProcessorEntity campaignEntity) {
        String targetHash = campaignEntity.getHash();
        Object lock = LOCK_CACHE.getUnchecked(targetHash);

        synchronized (lock) {
            // 业务代码
            // ...
        }
        return campaignEntity;
    }
}

这个方案会自动清理长时间未使用的锁对象,避免内存泄漏,但需要引入Guava库。如果不想引入第三方库,也可以定期手动清理Map里的过期锁对象,但实现起来会麻烦一些。

内容的提问来源于stack exchange,提问作者Don Jose

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:36:24