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

Java单例类锁问题排查:锁产生原因与风险分析

问题分析与解答

先贴出相关代码:

单例类代码

public class SomeSingleton {

  private static final Thing object = new Thing();

    public static SomeSingleton instance = null;    

    private final Properties logfiles = new Properties();

    public static SomeSingleton getInstance() {
        if (instance == null) {
            createInstance();
        }
        return instance;
    }

 
    /**
     * Imagine this method called
     * SomeSingleton.getInstance().log()
     */
    public void log(final String message) {
        try {
            synchronized (logfiles) {
                // 此处应有实际日志操作,比如写入文件
            }
        } catch (Exception e) {
            // 省略异常处理
        }
    }
}

Servlet代码

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    SomeSingleton.getInstance().log("some message");
}

1. 锁占用是否仅由单例实现方式导致?

不是。监控显示的锁占用主要来自log方法中synchronized (logfiles)的锁竞争,而非单例实现本身:

  • 单例实现的问题(线程不安全的getInstance、instance设为public)会导致多实例创建风险,但不会直接造成锁占用过高。
  • 真正的锁占用来源是:所有请求线程都会调用log方法,竞争同一个logfiles对象的锁(如果单例正常工作,所有线程共用同一个实例的logfiles)。如果synchronized块内有耗时操作(比如磁盘IO写日志),会导致大量线程阻塞等待锁,这才是锁占用高的核心原因。

2. 存在锁的情况下是否会出现竞态条件?

会,且存在两处竞态:

  • 单例初始化的竞态:getInstance方法没有同步控制,多线程可以同时进入if (instance == null)分支,多次调用createInstance(),最终可能创建多个SomeSingleton实例。再加上instance是public修饰,外部可以直接修改这个静态变量,进一步加剧线程安全问题。
  • 日志操作的潜在竞态:如果synchronized块内的日志操作没有被正确同步(比如省略了synchronized直接操作logfiles,或者块内有多个非原子操作),即使有锁,也可能出现日志内容错乱、数据不一致的问题。不过当前代码里synchronized块是空的,若补全实际操作,必须确保所有共享资源的读写都在锁范围内。

3. 是否会导致执行时长过高?

会,尤其是高并发场景下:

  • 如果synchronized块内包含磁盘IO、网络请求等耗时操作,所有请求线程必须串行执行这段代码,大量线程会阻塞在锁等待队列中,每个请求的执行时间会被大幅拉长。
  • 单例初始化的竞态如果导致多次实例创建,虽然会分散锁竞争(每个实例有自己的logfiles锁),但这违背了单例设计初衷,还会造成资源浪费,间接影响系统性能。

内容的提问来源于stack exchange,提问作者Berlin Brown

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 20:17:11