单例初始化中的耗时操作如何拖死主线程

目录

问题本质

[场景一:主线程首次初始化,直接 ANR](#场景一:主线程首次初始化,直接 ANR)

代码

原因

[场景二:后台线程首次初始化,也可能导致 ANR](#场景二:后台线程首次初始化,也可能导致 ANR)

代码

现象

原因

场景三:基于场景二我们在将耗时操作放在其他子线程(避免ANR)

代码

现象

结论

更危险的情况:耗时操作还依赖主线程

[最小复现 Demo](#最小复现 Demo)

[示例 1:主线程直接卡死](#示例 1:主线程直接卡死)

[示例 2:子线程先持锁,主线程再等锁](#示例 2:子线程先持锁,主线程再等锁)

预期效果

如何判断问题是不是单例初始化导致的

[1. ANR 栈里有 getInstance()](#1. ANR 栈里有 getInstance())

[2. 构造函数里有重操作](#2. 构造函数里有重操作)

[3. 首次初始化和用户操作时间点接近](#3. 首次初始化和用户操作时间点接近)

[4. 日志显示耗时发生在单例首次初始化阶段](#4. 日志显示耗时发生在单例首次初始化阶段)

为什么"改成异步"通常能显著缓解

反例

更安全的思路

修复建议

结论

相关推荐


问题本质

很多人对单例的直觉是:

  • 只是 getInstance()
  • 应该很轻
  • 就算慢,也不过是某个线程慢一点

但真实情况是:

单例的首次初始化通常自带"全局串行化"语义。

例如常见写法:

java 复制代码
public class SCCManager {
    private static volatile SCCManager instance;

    private SCCManager() {
        initSomethingHeavy();
    }

    public static SCCManager getInstance() {
        if (instance == null) {
            synchronized (SCCManager) {
                if (instance == null) {
                    instance = new SCCManager();
                }
            }
        }
        return instance;
    }
}

问题在于:

  • new SCCManager() 发生在锁内
  • 构造函数里如果有耗时逻辑
  • 那这个耗时就不再只是"某个方法慢"
  • 而会变成"任何访问这个单例的线程都可能被一起堵住"

场景一:主线程首次初始化,直接 ANR

这是最容易理解的一类。

代码

java 复制代码
 package com.kuen.beautifulchina.utils;

public class DemoManager {
    private static volatile DemoManager instance;

    private DemoManager() {
        LogUtil.e("DemoManager ANR", "构造方法Start");
        heavyInit();
        LogUtil.e("DemoManager ANR", "构造方法End");
    }

    public static DemoManager getInstance() {
        if (instance == null) {
            synchronized (DemoManager.class) {
                if (instance == null) {
                    instance = new DemoManager();
                }
            }
        }
        return instance;
    }

    private void heavyInit() {
        try {
            LogUtil.e("DemoManager ANR", "heavyInit.Start:"+"thread=" + Thread.currentThread().getName());
            Thread.sleep(8000);
            LogUtil.e("DemoManager ANR", "heavyInitEnd");
        } catch (InterruptedException ignored) {
        }
    }
}

主线程点击按钮时触发:

java 复制代码
Button testSingletonButton = findViewById(R.id.btn_test_demo_singleton);
if (testSingletonButton != null) {
    testSingletonButton.setOnClickListener(v -> {
        long start = SystemClock.elapsedRealtime();
        DemoManager.getInstance();
        long costMs = SystemClock.elapsedRealtime() - start;
        Toast.makeText(this, "DemoManager init/use cost: " + costMs + "ms", Toast.LENGTH_SHORT).show();
        LogUtil.e("DemoManager ANR", "DemoManager init/use cost: " + costMs + "ms");

    });
}

秒崩了

现象

  • 点击后 UI 卡住
  • 触摸事件无法及时处理
  • 最终出现:
java 复制代码
17:46:19.058 [DemoManager ANR] 构造方法Start
17:46:19.058 [DemoManager ANR] heavyInit.Start:thread=main
17:46:27.058 [DemoManager ANR] heavyInitEnd
17:46:27.058 [DemoManager ANR] 构造方法End
17:46:27.066 [DemoManager ANR] DemoManager init/use cost: 8001ms

17:46:28.579 ANR in com.kuen.beautifulchina (com.kuen.beautifulchina/.MainActivity)
             PID: 718
             Reason: Input dispatching timed out (8a2aba2 com.kuen.beautifulchina/com.kuen.beautifulchina.MainActivity is not responding. Waited 5000ms for MotionEvent(action=DOWN)).
             Parent: com.kuen.beautifulchina/.MainActivity
             ErrorId: 2cee0826-bd4c-4cc3-8a6f-8660a374a727
             Frozen: false
             Board Sensor Temperature: 31197
             Current cpu freq: [cpu0: 1459200, cpu1: 1459200, cpu2: 2707200, cpu3: 2707200, cpu4: 2707200, cpu5: 2707200, cpu6: 2707200, cpu7: 2553600]
             Load: 5.69 / 5.31 / 5.51
             ----- Output from /proc/pressure/memory -----
             some avg10=0.00 avg60=0.00 avg300=0.00 total=5299767481
             full avg10=0.00 avg60=0.00 avg300=0.00 total=4080343811
             ----- End output from /proc/pressure/memory -----
             ----- Output from /proc/pressure/cpu -----
             some avg10=13.18 avg60=12.65 avg300=11.43 total=152591673430
             full avg10=0.00 avg60=0.00 avg300=0.00 total=0
             ----- End output from /proc/pressure/cpu -----
             ----- Output from /proc/pressure/io -----
             some avg10=0.09 avg60=0.03 avg300=0.06 total=10666621151
             full avg10=0.00 avg60=0.00 avg300=0.00 total=6180883458
             ----- End output from /proc/pressure/io -----
             
             CPU usage from 151881ms to 0ms ago (2026-03-25 17:43:52.522 to 2026-03-25 17:46:24.403):
               10% 2215/surfaceflinger: 4.9% user + 5.4% kernel / faults: 214 minor 13 major
                 4.2% 2605/app: 2.1% user + 2.1% kernel
                 1.7% 2215/surfaceflinger: 1% user + 0.6% kernel
                 1% 2603/TimerDispatch: 0.3% user + 0.7% kernel
                 0.8% 2490/binder:2215_1: 0.2% user + 0.6% kernel
                 0.6% 5727/binder:2215_4: 0.1% user + 0.5% kernel
                 0.5% 2503/RenderEngine: 0.3% user + 0.1% kernel
                 0.3% 2491/binder:2215_2: 0.1% user + 0.2% kernel
                 0.2% 2666/RelBufCB: 0% user + 0.1% kernel
                 0.1% 2657/binder:2215_3: 0% user + 0% kernel
                 0.1% 2608/RegionSampling: 0% user + 0% kernel
                 0.1% 2665/BckgrndExec HP: 0% user + 0% kernel
                 0% 2638/FpsStatTimer: 0% user + 0% kernel
                 0% 8329/binder:2215_5: 0% user + 0% kernel
                 0% 7162/BckgrndExec LP: 0% user + 0% kernel
                 0% 2609/RegSampIdle: 0% user + 0% kernel
                 0% 2607/appSf: 0% user + 0% kernel
                 0% 2613/UpdateImminentT: 0% user + 0% kernel
                 0% 2612/IdleTimer: 0% user + 0% kernel
                 0% 2667/passBlur: 0% user + 0% kernel
                 0% 9060/2215-binder-0: 0% user + 0% kernel
               7.5% 2593/system_server: 3.7% user + 3.7% kernel / faults: 19392 minor 36 major
                 0.4% 5248/MiuiWifiService: 0.1% user + 0.3% kernel
                 0.3% 4943/SensorService: 0.2% user + 0% kernel
                 0.3% 5241/WifiHandlerThre: 0.2% user + 0.1% kernel
                 0.3% 6459/binder:2593_B: 0.1% user + 0.1% kernel
                 0.3% 2640/Signal Catcher: 0.1% user + 0.1% kernel
                 0.2% 5888/SsruCpuTh: 0% user + 0.2% kernel
                 0.2% 2705/ActivityManager: 0.1% user + 0.1% kernel
                 0.2% 4950/eduling.default: 0.2% user + 0% kernel
                 0.2% 4957/SettingsProvide: 0.1% user + 0% kernel
                 0.1% 5489/SmartPowerServi: 0% user + 0.1% kernel
                 0.1% 7795/binder:2593_13: 0.1% user + 0% kernel
                 0.1% 5951/binder:2593_9: 0% user + 0% kernel
                 0.1% 4995/hidl_ssvc_poll: 0% user + 0.1% kernel
                 0.1% 16980/binder:2593_1C: 0% user + 0% kernel
                 0.1% 5235/NetworkStats: 0.1% user + 0% kernel
                 0.1% 5458/PowerStrategyMo: 0% user + 0% kernel
                 0.1% 2653/HeapTaskDaemon: 0.1% user + 0% kernel
                 0.1% 4990/InputDispatcher: 0% user + 0% kernel
                 0.1% 2701/android.bg: 0% user + 0.1% kernel
                 0.1% 2593/system_server: 0% user + 0% kernel
                 0.1% 7797/binder:2593_15: 0% user + 0% kernel
                 0.1% 5641/BarFollowAnimat: 0% user + 0% kernel
                 0.1% 7465/binder:2593_12: 0% user + 0% kernel
                 0% 2687/android.ui: 0% user + 0% kernel
                 0% 2690/android.anim: 0% user + 0% kernel
                 0% 5238/MiuiNetworkPoli: 0% user + 0% kernel
                 0% 5493/miui-gesture: 0% user + 0% kernel
                 0% 4927/binder:2593_5: 0% user + 0% kernel
                 0% 7839/binder:2593_18: 0% user + 0% kernel
                 0% 2647/system_server: 0% user + 0% kernel
                 0% 2674/android.fg: 0% user + 0% kernel
                 0% 2680/BpfServicePoll: 0% user + 0% kernel
17:46:28.580    +0% 2602/pool-6-thread-1: 0% user + 0% kernel
                +0% 2604/AnrMainProcessD: 0% user + 0% kernel
                +0% 2614/AnrMainProcessD: 0% user + 0% kernel
                +0% 2628/AnrConsumer: 0% user + 0% kernel
                +0% 2633/AnrAuxiliaryTas: 0% user + 0% kernel
                +0% 2634/pool-3-thread-1: 0% user + 0% kernel
               4.9% 3175/adbd: 1.2% user + 3.7% kernel / faults: 7419 minor
                 2.3% 1164/UsbFfs-worker: 0.2% user + 2.1% kernel
                 1.8% 3175/adbd: 0.9% user + 0.9% kernel
                 0.3% 1547/shell svc 1546: 0% user + 0.3% kernel
                 0.3% 1097/shell svc 1092: 0% user + 0.3% kernel
               4.2% 26922/com.android.quicksearchbox: 2.2% user + 1.9% kernel / faults: 1141 minor 90 major
                 4.1% 26922/.quicksearchbox: 2.2% user + 1.8% kernel
                 0% 460/MemoryInfra: 0% user + 0% kernel
                 0% 459/Chrome_IOThread: 0% user + 0% kernel
                 0% 27097/RxSchedulerPurg: 0% user + 0% kernel
                 0% 27127/pool-12-thread-: 0% user + 0% kernel
                 0% 455/ThreadPoolServi: 0% user + 0% kernel
                 0% 456/ThreadPoolForeg: 0% user + 0% kernel
                 0% 8245/binder:26922_9: 0% user + 0% kernel
               3.4% 6287/com.android.systemui: 1.8% user + 1.5% kernel / faults: 2025 minor 51 major
                 2.5% 6287/ndroid.systemui: 1.4% user + 1.1% kernel
                 0.3% 7120/RenderThread: 0.1% user + 0.1% kernel
                 0.1% 6315/Signal Catcher: 0.1% user + 0% kernel
                 0% 6799/wmshell.main: 0% user + 0% kernel
                 0% 7011/shell.secondary: 0% user + 0% kernel
                 0% 7805/binder:6287_9: 0% user + 0% kernel
                 0% 16690/binder:6287_E: 0% user + 0% kernel
                 0% 7595/CCBackground: 0% user + 0% kernel
                 0% 14371/binder:6287_C: 0% user + 0% kernel
                 0% 7117/hwuiTask0: 0% user + 0% kernel
                 0% 7118/hwuiTask1: 0% user + 0% kernel
                 0% 7207/blastAsyncWork: 0% user + 0% kernel
                 0% 22412/DefaultDispatch: 0% user + 0% kernel
                 0% 23967/binder:6287_10: 0% user + 0% kernel
                 0% 6328/binder:6287_2: 0% user + 0% kernel
                 0% 6575/6287-ScoutState: 0% user + 0% kernel
                 0% 7096/ll.splashscreen: 0% user + 0% kernel
                 0% 7129/onetrack_servic: 0% user + 0% kernel
                 0% 7150/BroadcastRunnin: 0% user + 0% kernel
                 0% 7153/SystemUIBg-1: 0% user + 0% kernel
                 0% 7154/SystemUIBg-2: 0% user + 0% kernel
                 0% 7155/SystemUIBg-3: 0% user + 0% kernel
                 0% 7158/SystemUIBg-6: 0% user + 0% kernel
                 0% 8136/binder:6287_A: 0% user + 0% kernel
                 0% 14513/binder:6287_D: 0% user + 0% kernel
               0.9% 718/com.kuen.beautifulchina: 0.7% user + 0.2% kernel / faults: 44795 minor 6 major
                 0.1% 718/usap64: 0% user + 0% kernel
                +0% 2240/Signal Catcher: 0% user + 0% kernel
                +0% 2241/perfetto_hprof_: 0% user + 0% kernel
                +0% 2243/ADB-JDWP Connec: 0% user + 0% kernel
                +0% 2248/Jit thread pool: 0% user + 0% kernel
                +0% 2253/HeapTaskDaemon: 0% user + 0% kernel
                +0% 2254/ReferenceQueueD: 0% user + 0% kernel
                +0% 2256/FinalizerDaemon: 0% user + 0% kernel
                +0% 2259/FinalizerWatchd: 0% user + 0% kernel
                +0% 2260/binder:718_1: 0% user + 0% kernel
                +0% 2261/binder:718_2: 0% user + 0% kernel
                +0% 2302/binder:718_3: 0% user + 0% kernel
                +0% 2303/718-ScoutStateM: 0% user + 0% kernel
                +0% 2304/binder:718_4: 0% user + 0% kernel
                +0% 2348/ActivityHelper: 0% user + 0% kernel
                +0% 2351/Profile Saver: 0% user + 0% kernel
                +0% 2355/MiuiMonitorThre: 0% user + 0% kernel
                +0% 2357/Timer-0: 0% user + 0% kernel
                +0% 2358/Timer-1: 0% user + 0% kernel
                +0% 2359/AppCustomScenar: 0% user + 0% kernel
                +0% 2363/RenderThread: 0% user + 0% kernel
                +0% 2368/AICollector: 0% user + 0% kernel
                +0% 2372/Binder:intercep: 0% user + 0% kernel
                +0% 2374/pool-3-thread-1: 0% user + 0% kernel
                +0% 2377/TracingMuxer: 0% user + 0% kernel
                +0% 2379/FramePredictIni: 0% user + 0% kernel
                +0% 2380/hwuiTask0: 0% user + 0% kernel
                +0% 2381/hwuiTask1: 0% user + 0% kernel
                +0% 2384/VsyncReceiver: 0% user + 0% kernel
                +0% 2387/DefaultDispatch: 0% user + 0% kernel
                +0% 2388/DefaultDispatch: 0% user + 0% kernel
                +0% 2389/DefaultDispatch: 0% user + 0% kernel
                +0% 2390/DefaultDispatch: 0% user + 0% kernel
                +0% 2391/DefaultDispatch: 0% user + 0% kernel
17:46:28.580    +0% 2392/DefaultDispatch: 0% user + 0% kernel
                +0% 2393/DefaultDispatch: 0% user + 0% kernel
                +0% 2394/DefaultDispatch: 0% user + 0% kernel
                +0% 2395/SurfaceSyncGrou: 0% user + 0% kernel
                +0% 2396/DefaultDispatch: 0% user + 0% kernel
                +0% 2399/blastAsyncWork: 0% user + 0% kernel
                +0% 2401/OkHttp Dispatch: 0% user + 0% kernel
                +0% 2402/OkHttp Dispatch: 0% user + 0% kernel
                +0% 2403/OkHttp Dispatch: 0% user + 0% kernel
                +0% 2404/OkHttp Dispatch: 0% user + 0% kernel
                +0% 2450/ConscryptStatsL: 0% user + 0% kernel
                +0% 2452/uaici.github.io: 0% user + 0% kernel
                +0% 2453/OkHttp TaskRunn: 0% user + 0% kernel
                +0% 2454/OkHttp TaskRunn: 0% user + 0% kernel
                +0% 2455/OkHttp TaskRunn: 0% user + 0% kernel
                +0% 2456/OkHttp TaskRunn: 0% user + 0% kernel
                +0% 2457/OkHttp TaskRunn: 0% user + 0% kernel
                +0% 2458/Okio Watchdog: 0% user + 0% kernel
                +0% 2459/GrallocUploadTh: 0% user + 0% kernel
               2.1% 881/logd: 0.3% user + 1.8% kernel / faults: 174 minor
                 0.8% 1208/logd.writer: 0.1% user + 0.7% kernel
                 0.5% 1098/logd.reader.per: 0% user + 0.5% kernel
                 0.5% 1549/logd.reader.per: 0% user + 0.5% kernel
                 0% 1209/logd.control: 0% user + 0% kernel
                 0% 1212/logd.auditd: 0% user + 0% kernel
                 0% 21963/logd.reader.per: 0% user + 0% kernel
               1.5% 1976/vendor.qti.hardware.display.composer-service: 0.4% user + 1% kernel / faults: 1882 minor 2 major
                 0.5% 2432/vendor.qti.hard: 0.2% user + 0.3% kernel
                 0.4% 1878/binder:1976_5: 0.2% user + 0.2% kernel
                 0.2% 2588/binder:1976_1: 0.1% user + 0.1% kernel
                 0% 2659/trigger_cwb: 0% user + 0% kernel
                 0% 2583/SDM_EventThread: 0% user + 0% kernel
                 0% 5143/binder:1976_1: 0% user + 0% kernel
                 0% 31478/binder:1976_5: 0% user + 0% kernel
                 0% 2591/binder:1976_1: 0% user + 0% kernel
                 0% 2658/binder:1976_1: 0% user + 0% kernel
                 0% 2590/binder:1976_1: 0% user + 0% kernel
                 0% 2924/DPPS_THREAD: 0% user + 0% kernel
               1.4% 27409/irq/258-dwc3: 0% user + 1.4% kernel
               0.9% 639/kworker/u16:6-mi_sched: 0% user + 0.9% kernel / faults: 17 minor
             8.2% TOTAL: 2.8% user + 4.1% kernel + 0% iowait + 1% irq + 0.1% softirq
             CPU usage from 95ms to 462ms later (2026-03-25 17:46:24.498 to 2026-03-25 17:46:24.865):
               150% 2593/system_server: 59% user + 91% kernel / faults: 1900 minor 3 major
                 91% 2640/Signal Catcher: 47% user + 44% kernel
                 41% 2633/AnrAuxiliaryTas: 8.8% user + 32% kernel
                 2.9% 2689/android.display: 2.9% user + 0% kernel
                 2.9% 2701/android.bg: 2.9% user + 0% kernel
                 2.9% 2948/PackageManagerB: 0% user + 2.9% kernel
                 2.9% 4958/CachedAppOptimi: 2.9% user + 0% kernel
                 2.9% 4979/DownscaleContro: 2.9% user + 0% kernel
                 2.9% 6931/MiWillWorkHandl: 0% user + 2.9% kernel
                 2.9% 6947/MscsService: 0% user + 2.9% kernel
                 2.9% 6948/CameraDetectSer: 0% user + 2.9% kernel
               30% 718/com.kuen.beautifulchina: 21% user + 8.2% kernel
                 30% 2363/RenderThread: 21% user + 8.2% kernel
                 5.4% 2304/binder:718_4: 2.7% user + 2.7% kernel
               32% 2215/surfaceflinger: 17% user + 14% kernel
                 14% 2215/surfaceflinger: 8.8% user + 5.8% kernel
                 5.8% 2603/TimerDispatch: 2.9% user + 2.9% kernel
                 5.8% 2657/binder:2215_3: 2.9% user + 2.9% kernel
                 2.9% 2666/RelBufCB: 0% user + 2.9% kernel
               17% 1976/vendor.qti.hardware.display.composer-service: 8.6% user + 8.6% kernel
                 11% 1878/binder:1976_5: 5.7% user + 5.7% kernel
                 8.6% 2432/vendor.qti.hard: 0% user + 8.6% kernel
               8.4% 1426/crtc_commit:203: 0% user + 8.4% kernel
               6.2% 3175/adbd: 3.1% user + 3.1% kernel
                 3.1% 1164/UsbFfs-worker: 0% user + 3.1% kernel
                 3.1% 3175/adbd: 3.1% user + 0% kernel
               2.7% 14/rcu_preempt: 0% user + 2.7% kernel
               2.7% 15/rcuog/0: 0% user + 2.7% kernel
               2.7% 66/rcuop/6: 0% user + 2.7% kernel
               2.7% 73/rcuop/7: 0% user + 2.7% kernel
               2.7% 881/logd: 0% user + 2.7% kernel
                 2.7% 1208/logd.writer: 2.7% user + 0% kernel
                 2.7% 1549/logd.reader.per: 0% user + 2.7% kernel
               2.7% 1255/kgsl_hwsched: 0% user + 2.7% kernel
               2.8% 1546/logcat: 2.8% user + 0% kernel

原因

因为这次首次初始化发生在主线程:

  • 主线程进入 getInstance()
  • 主线程进入构造函数
  • 主线程在 heavyInit() 里阻塞 8 秒
  • 输入事件分发超时
  • 触发 ANR

场景二:后台线程首次初始化,也可能导致 ANR

这类更隐蔽,也更容易在真实项目里出现。

很多人会觉得:

"耗时在子线程做,不就安全了吗?"

不一定。

代码

复制代码
public class DemoManager {
    private static volatile DemoManager instance;

    private DemoManager() {
        heavyInit();
    }

    public static DemoManager getInstance() {
        if (instance == null) {
            synchronized (DemoManager.class) {
                if (instance == null) {
                    instance = new DemoManager();
                }
            }
        }
        return instance;
    }

    private void heavyInit() {
        try {
            System.out.println("thread=" + Thread.currentThread().getName());
            Thread.sleep(8000);
        } catch (InterruptedException ignored) {
        }
    }
}

先由后台线程触发:

复制代码
Executors.newSingleThreadExecutor().execute(() -> {
    DemoManager.getInstance();
});

然后主线程稍后访问:

java 复制代码
Button testSingletonButton = findViewById(R.id.btn_test_demo_singleton);
if (testSingletonButton != null) {
    testSingletonButton.setOnClickListener(v -> {
        long start = SystemClock.elapsedRealtime();
        DemoManager.getInstance();
        long costMs = SystemClock.elapsedRealtime() - start;
        Toast.makeText(this, "DemoManager init/use cost: " + costMs + "ms", Toast.LENGTH_SHORT).show();
        LogUtil.e("DemoManager ANR", "DemoManager init/use cost: " + costMs + "ms");

    });
}

现象

  1. heavyInit() 很快结束

    两条 heavyInit.Start 都打在 pool-3-thread-1 上,且紧接着就出现 构造方法End,说明 execute() 提交任务后立刻返回,没有在调用线程里 sleep 8 秒。

  2. 真正耗时在另一个线程里
    ThreadExecutor.Start 出现在 pool-4-thread-1,约 8 秒后 才出现 ThreadExecutorEnd,与 Thread.sleep(8000) 一致,说明 8 秒阻塞发生在 Executor 的工作线程,而不是 heavyInit 的调用线程。

  3. DemoManager init/use cost: 0ms 多次

    多次点击后耗时为 0ms,是因为 单例已创建完成,后续 getInstance() 只做「取已有实例」,不再执行构造里的 heavyInit()(或不再经历首次初始化路径)。

  4. 与场景二对比

    若把 sleep 放在主线程/构造同步路径里,会出现长时间卡顿甚至 ANR;场景三里 主线程(若从主线程调 getInstance)不会被这 8 秒拖住------你这份日志里构造线程名是 pool-3-thread-1,说明当前测试里 getInstance() 可能是在某个线程池线程上触发的,但结论不变:sleep 不在提交 Runnable 的那条逻辑链上同步执行。

日志里可能看到:

java 复制代码
17:39:10.640 [DemoManager ANR] 构造方法Start
17:39:10.640 [DemoManager ANR] heavyInit.Start:thread=pool-3-thread-1
17:39:18.641 [DemoManager ANR] heavyInitEnd
17:39:18.641 [DemoManager ANR] 构造方法End
17:39:18.645 [DemoManager ANR] DemoManager init/use cost: 5962ms

17:39:18.685 [DemoManager ANR] DemoManager init/use cost: 0ms
17:39:20.259 ANR in com.kuen.beautifulchina (com.kuen.beautifulchina/.MainActivity)
             PID: 32071
             Reason: Input dispatching timed out (54a654f com.kuen.beautifulchina/com.kuen.beautifulchina.MainActivity is not responding. Waited 5000ms for MotionEvent(action=DOWN)).
             Parent: com.kuen.beautifulchina/.MainActivity
             ErrorId: d5af7d7e-d727-4e0c-a4c2-4645fef3a6e2
             Frozen: false
             Board Sensor Temperature: 31627
             Current cpu freq: [cpu0: 1804800, cpu1: 1804800, cpu2: 1708800, cpu3: 1708800, cpu4: 1708800, cpu5: 1075200, cpu6: 1075200, cpu7: 1248000]
             Load: 6.45 / 5.78 / 5.76
             ----- Output from /proc/pressure/memory -----
             some avg10=0.00 avg60=0.03 avg300=0.14 total=5299666798
             full avg10=0.00 avg60=0.02 avg300=0.10 total=4080265367
             ----- End output from /proc/pressure/memory -----
             ----- Output from /proc/pressure/cpu -----
             some avg10=13.29 avg60=11.24 avg300=11.85 total=152543817278
             full avg10=0.00 avg60=0.00 avg300=0.00 total=0
             ----- End output from /proc/pressure/cpu -----
             ----- Output from /proc/pressure/io -----
             some avg10=0.22 avg60=0.15 avg300=0.25 total=10665752428
             full avg10=0.00 avg60=0.01 avg300=0.06 total=6180416338
             ----- End output from /proc/pressure/io -----
             
             CPU usage from 130800ms to -1ms ago (2026-03-25 17:37:07.134 to 2026-03-25 17:39:17.935):
               9.2% 2593/system_server: 4.5% user + 4.7% kernel / faults: 26721 minor 259 major
                 0.4% 2640/Signal Catcher: 0.2% user + 0.1% kernel
                 0.4% 5248/MiuiWifiService: 0.1% user + 0.3% kernel
                 0.4% 2705/ActivityManager: 0.2% user + 0.1% kernel
                 0.4% 4943/SensorService: 0.2% user + 0.1% kernel
                 0.3% 7806/binder:2593_17: 0.1% user + 0.1% kernel
                 0.3% 17208/binder:2593_1E: 0.1% user + 0.1% kernel
                 0.3% 4957/SettingsProvide: 0.1% user + 0.1% kernel
                 0.3% 5489/SmartPowerServi: 0% user + 0.2% kernel
                 0.3% 5241/WifiHandlerThre: 0.1% user + 0.1% kernel
                 0.2% 4950/eduling.default: 0.2% user + 0% kernel
                 0.2% 5235/NetworkStats: 0.1% user + 0% kernel
                 0.2% 2593/system_server: 0.1% user + 0% kernel
                 0.1% 5793/binder:2593_6: 0% user + 0% kernel
                 0.1% 16034/binder:2593_19: 0% user + 0% kernel
                 0.1% 18078/binder:2593_20: 0.1% user + 0% kernel
                 0.1% 2701/android.bg: 0% user + 0.1% kernel
                 0.1% 5458/PowerStrategyMo: 0.1% user + 0% kernel
                 0.1% 4995/hidl_ssvc_poll: 0% user + 0.1% kernel
                 0.1% 5641/BarFollowAnimat: 0% user + 0% kernel
                 0.1% 4990/InputDispatcher: 0% user + 0% kernel
                 0.1% 5888/SsruCpuTh: 0% user + 0.1% kernel
                 0.1% 2653/HeapTaskDaemon: 0.1% user + 0% kernel
                 0.1% 2687/android.ui: 0% user + 0% kernel
                 0.1% 2690/android.anim: 0% user + 0% kernel
                 0.1% 17648/binder:2593_1F: 0% user + 0% kernel
                 0.1% 5238/MiuiNetworkPoli: 0% user + 0% kernel
                 0.1% 6630/MobileDataStats: 0% user + 0% kernel
                 0% 2674/android.fg: 0% user + 0% kernel
                 0% 2689/android.display: 0% user + 0% kernel
                 0% 2691/android.anim.lf: 0% user + 0% kernel
                 0% 6458/binder:2593_A: 0% user + 0% kernel
                 0% 5493/miui-gesture: 0% user + 0% kernel
                 0% 2663/binder:2593_1: 0% user + 0% kernel
                 0% 2647/system_server: 0% user + 0% kernel
                 0% 2680/BpfServicePoll: 0% user + 0% kernel
                 0% 5457/Greezer: 0% user + 0% kernel
                 0% 7141/binder:2593_10: 0% user + 0% kernel
                 0% 2682/BpfServiceTr: 0% user + 0% kernel
                 0% 2688/android.io: 0% user + 0% kernel
                 0% 2821/batterystats-ha: 0% user + 0% kernel
                 0% 5951/binder:2593_9: 0% user + 0% kernel
                 0% 6945/NetworkAccelera: 0% user + 0% kernel
                 0% 4927/binder:2593_5: 0% user + 0% kernel
                 0% 5497/SchedBoostServi: 0% user + 0% kernel
                 0% 7798/binder:2593_16: 0% user + 0% kernel
                 0% 2715/OomAdjuster: 0% user + 0% kernel
                 0% 4991/InputReader: 0% user + 0% kernel
                 0% 2891/PowerManagerSer: 0% user + 0% kernel
                 0% 4965/AlarmManager: 0% user + 0% kernel
                 0% 5253/ConnectivitySer: 0% user + 0% kernel
                 0% 5484/BpfClientLib: 0% user + 0% kernel
                 0% 5635/TaskSnapshotPer: 0% user + 0% kernel
                 0% 17048/binder:2593_1D: 0% user + 0% kernel
                 0% 2695/ScoutSystemServ: 0% user + 0% kernel
17:39:20.259    +0% 698/DefaultDispatch: 0% user + 0% kernel
                +0% 699/DefaultDispatch: 0% user + 0% kernel
                +0% 700/DefaultDispatch: 0% user + 0% kernel
                +0% 701/SurfaceSyncGrou: 0% user + 0% kernel
                +0% 703/blastAsyncWork: 0% user + 0% kernel
                +0% 705/GrallocUploadTh: 0% user + 0% kernel
               2.2% 881/logd: 0.4% user + 1.8% kernel / faults: 621 minor 12 major
                 0.9% 1208/logd.writer: 0.1% user + 0.7% kernel
                 0.6% 22774/logd.reader.per: 0% user + 0.5% kernel
                 0.5% 1549/logd.reader.per: 0% user + 0.5% kernel
                 0% 1212/logd.auditd: 0% user + 0% kernel
                 0% 1209/logd.control: 0% user + 0% kernel
                 0% 21963/logd.reader.per: 0% user + 0% kernel
               1.8% 1976/vendor.qti.hardware.display.composer-service: 0.5% user + 1.2% kernel / faults: 900 minor 4 major
                 0.7% 2432/vendor.qti.hard: 0.2% user + 0.4% kernel
                 0.4% 1878/binder:1976_5: 0.2% user + 0.2% kernel
                 0.4% 2588/binder:1976_1: 0.2% user + 0.1% kernel
                 0% 2659/trigger_cwb: 0% user + 0% kernel
                 0% 5143/binder:1976_1: 0% user + 0% kernel
                 0% 2583/SDM_EventThread: 0% user + 0% kernel
                 0% 2591/binder:1976_1: 0% user + 0% kernel
                 0% 2658/binder:1976_1: 0% user + 0% kernel
                 0% 31478/binder:1976_5: 0% user + 0% kernel
                 0% 2590/binder:1976_1: 0% user + 0% kernel
                 0% 2934/LTM_THREAD: 0% user + 0% kernel
               1.4% 27409/irq/258-dwc3: 0% user + 1.4% kernel
               1.2% 23911/com.miui.home: 0.9% user + 0.3% kernel / faults: 2591 minor 77 major
                 0.7% 24197/RenderThread: 0.6% user + 0.1% kernel
                 0.2% 23911/com.miui.home: 0.2% user + 0% kernel
                 0% 23923/HeapTaskDaemon: 0% user + 0% kernel
                 0% 30916/binder:23911_B: 0% user + 0% kernel
                 0% 23924/ReferenceQueueD: 0% user + 0% kernel
                 0% 24030/FsGestureSecond: 0% user + 0% kernel
                 0% 24119/system_imp_cach: 0% user + 0% kernel
                 0% 24620/binder:23911_8: 0% user + 0% kernel
                 0% 25965/passblur: 0% user + 0% kernel
                 0% 23935/binder:23911_2: 0% user + 0% kernel
                 0% 24043/launcher-loader: 0% user + 0% kernel
                 0% 24053/ckground-pool-1: 0% user + 0% kernel
                 0% 24184/hwuiTask0: 0% user + 0% kernel
                 0% 24265/Thread_ReloadTa: 0% user + 0% kernel
                 0% 25177/blastAsyncWork: 0% user + 0% kernel
                 0% 32278/binder:23911_F: 0% user + 0% kernel
                +0% 606/onetrack_track_: 0% user + 0% kernel
               1.1% 1426/crtc_commit:203: 0% user + 1.1% kernel
             8.9% TOTAL: 3.1% user + 4.3% kernel + 0.1% iowait + 1% irq + 0.1% softirq
             CPU usage from 98ms to 480ms later (2026-03-25 17:39:18.032 to 2026-03-25 17:39:18.414):
               159% 2593/system_server: 73% user + 85% kernel / faults: 2055 minor 9 major
                 90% 2640/Signal Catcher: 56% user + 34% kernel
                 39% 761/AnrAuxiliaryTas: 8.5% user + 31% kernel
                 2.8% 4991/InputReader: 0% user + 2.8% kernel
                 2.8% 4995/hidl_ssvc_poll: 0% user + 2.8% kernel
                 2.8% 5238/MiuiNetworkPoli: 0% user + 2.8% kernel
                 2.8% 5683/LongRangeMethod: 0% user + 2.8% kernel
                 2.8% 6039/ProcessMemoryCl: 2.8% user + 0% kernel
               28% 2215/surfaceflinger: 16% user + 11% kernel
                 16% 2215/surfaceflinger: 11% user + 5.6% kernel
                 2.8% 2603/TimerDispatch: 0% user + 2.8% kernel
                 2.8% 2605/app: 2.8% user + 0% kernel
                 2.8% 2657/binder:2215_3: 0% user + 2.8% kernel
                 2.8% 2666/RelBufCB: 2.8% user + 0% kernel
                 2.8% 5727/binder:2215_4: 2.8% user + 0% kernel
               30% 32071/com.kuen.beautifulchina: 21% user + 8.5% kernel
                 30% 680/RenderThread: 21% user + 8.5% kernel
               16% 1976/vendor.qti.hardware.display.composer-service: 8.3% user + 8.3% kernel
                 8.3% 1878/binder:1976_5: 5.5% user + 2.7% kernel
                 5.5% 2432/vendor.qti.hard: 0% user + 5.5% kernel
                 2.7% 2658/binder:1976_1: 0% user + 2.7% kernel
               5.2% 881/logd: 2.6% user + 2.6% kernel
                 2.6% 1549/logd.reader.per: 0% user + 2.6% kernel
               5.3% 1420/irq/319-xiaomi_tpfocaltech_ts: 0% user + 5.3% kernel
               5.3% 1426/crtc_commit:203: 0% user + 5.3% kernel
               5.6% 2156/vendor.xiaomi.hw.touchfeature-service: 2.8% user + 2.8% kernel
                 2.8% 2268/touch-main-loop: 2.8% user + 0% kernel
                 2.8% 2837/touch-frame-fi: 0% user + 2.8% kernel

说明耗时初始化确实在后台线程执行。

但主线程点击后仍可能 ANR。

原因

因为问题不只是"耗时在哪个线程执行",而是:

后台线程在初始化单例时持有锁。

流程变成:

  1. 后台线程先进入 getInstance()
  2. 后台线程拿到 DemoManager.class
  3. 后台线程在构造函数里做耗时操作
  4. 主线程随后也调用 getInstance()
  5. 主线程因为锁还没释放而被阻塞
  6. 主线程无法处理输入事件
  7. 最终 ANR

所以这类问题的本质是:

不是后台线程慢,而是后台线程在"持锁初始化"时变慢,把主线程一起拖住了。


场景三:基于场景二我们在将耗时操作放在其他子线程(避免ANR)

代码

java 复制代码
    private void heavyInit() {
        LogUtil.e("DemoManager ANR", "heavyInit.Start:"+"thread=" + Thread.currentThread().getName());
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    LogUtil.e("DemoManager ANR", "ThreadExecutor.Start:"+"thread=" + Thread.currentThread().getName());
                    Thread.sleep(8000);
                    LogUtil.e("DemoManager ANR", "ThreadExecutorEnd");
                } catch (InterruptedException ignored) {
                }
            }
        });
        LogUtil.e("DemoManager ANR", "heavyInit.Start:"+"thread=" + Thread.currentThread().getName());
    }

现象

  1. heavyInit() 很快结束

    两条 heavyInit.Start 都打在 pool-3-thread-1 上,且紧接着就出现 构造方法End ,说明 execute() 提交任务后立刻返回 ,没有在调用线程里 sleep 8 秒。

  2. 真正耗时在另一个线程里
    ThreadExecutor.Start 出现在 pool-4-thread-1 ,约 8 秒后 才出现 ThreadExecutorEnd,与 Thread.sleep(8000) 一致,说明 8 秒阻塞发生在 Executor 的工作线程 ,而不是 heavyInit 的调用线程。

  3. DemoManager init/use cost: 0ms 多次

    多次点击后耗时为 0ms,是因为 单例已创建完成 ,后续 getInstance() 只做「取已有实例」,不再执行构造里的 heavyInit()(或不再经历首次初始化路径)。

  4. 与场景二对比

    若把 sleep 放在主线程/构造同步路径里,会出现长时间卡顿甚至 ANR;场景三里 主线程(若从主线程调 getInstance)不会被这 8 秒拖住 ------你这份日志里构造线程名是 pool-3-thread-1,说明当前测试里 getInstance() 可能是在某个线程池线程上触发的 ,但结论不变:sleep 不在提交 Runnable 的那条逻辑链上同步执行

结论

  1. 场景三达到的目的 :把耗时操作(如 Thread.sleep、IO、重计算)放到 Executor / 后台线程避免在 Activity 主线程上同步执行 ,从而 降低 ANR 风险

  2. 与场景二的关系 :场景二若在 主线程 里做 sleep,会卡 UI;场景三把 sleep 挪到 子线程主线程可继续响应触摸、绘制

  3. 实现上仍需注意

    • 不要每次 new SingleThreadExecutor() 且不 shutdown() ,否则可能 线程泄漏 (这也是 IDE 提示 try-with-resources / 需关闭的原因);更稳妥是 应用级复用一个 ExecutorExecutorService ,在 Application.onTerminate 或明确生命周期里 shutdown()
    • 若耗时结束后要 更新 UI ,必须在 主线程runOnUiThread / Handler / LiveData 等),子线程里不能直接改 View。
  4. 验证方式 :看 Logcat 中 ThreadExecutor.Start / ThreadExecutorEnd 的线程名heavyInit/构造方法 的线程名 是否分离,以及 8 秒是否只夹在子线程日志之间,即可确认「耗时不在主线程同步路径上」。

更危险的情况:耗时操作还依赖主线程

如果构造函数里的耗时逻辑不只是 sleep,而是某些系统组件初始化,比如:

  • WebView
  • Chromium
  • Binder 同步调用
  • 主线程 Handler 配合完成的初始化链路

那问题会更复杂。

这时可能出现:

  1. 后台线程持有单例锁
  2. 后台线程在等待某个系统初始化完成
  3. 系统初始化过程又需要主线程参与
  4. 主线程却因为访问同一个单例被锁住
  5. 最终形成"互相等待"

这种场景通常比普通 sleep 更难排查,也更接近真实线上问题。


最小复现 Demo

下面是一份可以直接放进任意 Android demo 的伪代码。

示例 1:主线程直接卡死

java 复制代码
public class SlowSingleton {
    private static volatile SlowSingleton instance;

    private SlowSingleton() {
        slowInit();
    }

    public static SlowSingleton getInstance() {
        if (instance == null) {
            synchronized (SlowSingleton.class) {
                if (instance == null) {
                    instance = new SlowSingleton();
                }
            }
        }
        return instance;
    }

    private void slowInit() {
        try {
            Thread.sleep(8000);
        } catch (InterruptedException ignored) {
        }
    }
}
java 复制代码
findViewById(R.id.btn_main).setOnClickListener(v -> {
    SlowSingleton.getInstance();
});

示例 2:子线程先持锁,主线程再等锁

java 复制代码
findViewById(R.id.btn_background_first).setOnClickListener(v -> {
    Executors.newSingleThreadExecutor().execute(() -> {
        SlowSingleton.getInstance();
    });

    new Handler(Looper.getMainLooper()).postDelayed(() -> {
        SlowSingleton.getInstance();
    }, 200);
});

预期效果

  • 后台线程先拿到锁
  • 主线程 200ms 后访问单例
  • 主线程卡在 getInstance()
  • 最终输入事件超时,可能触发 ANR

如何判断问题是不是单例初始化导致的

排查时可以重点看这些特征。

1. ANR 栈里有 getInstance()

如果主线程栈反复出现:

  • xxxManager.getInstance()
  • synchronized
  • monitor contention

就很值得怀疑。

2. 构造函数里有重操作

例如:

  • Thread.sleep
  • 磁盘 I/O
  • 网络同步等待
  • WebView 初始化
  • 大对象反射/类加载
  • Binder 同步调用

3. 首次初始化和用户操作时间点接近

比如:

  • 点击按钮立刻卡住
  • 冷启动期间卡住
  • 初始化 SDK 时卡住

4. 日志显示耗时发生在单例首次初始化阶段

如果你打印线程和耗时,很容易看出来是不是发生在:

  • new Singleton()
  • getInstance()
  • 构造函数内部

为什么"改成异步"通常能显著缓解

如果把耗时逻辑从构造函数里拿掉,改成:

  • 构造函数只做轻量赋值
  • 耗时逻辑单独异步预热
  • 结果通过缓存或回填提供

那么单例初始化就不再持锁很久,主线程自然也不容易被拖住。

反例

java 复制代码
private Singleton() {
    expensiveInit();
}

更安全的思路

java 复制代码
private Singleton() {
    // 只做轻量初始化
}

public void warmUpAsync() {
    executor.execute(() -> expensiveInit());
}

修复建议

如果已经定位到类似问题,通常建议:

  1. 不要在单例构造函数里做重操作
  2. 不要在 getInstance() 的锁路径里执行耗时逻辑
  3. 把系统组件初始化改成异步预热
  4. 优先读取缓存,首次真实获取延后处理
  5. 对必须在主线程执行的逻辑,尽量避免放在全局共享对象的首次初始化阶段

结论

单例本身并不可怕,可怕的是:

把同步耗时操作塞进单例首次初始化路径。

一旦这样做,问题就不再是"某个方法慢",而会升级成:

  • 主线程直接卡死
  • 或后台线程持锁导致主线程等待
  • 甚至形成更复杂的线程互相依赖

最终表现出来的,就是 ANR。

当一个对象被设计成"全局唯一、首次初始化受锁保护"时,任何放进其构造路径的同步耗时操作,都会被放大成全局阻塞风险。

相关推荐

Android IdleHandler 原理解析与应用场景https://shuaici.blog.csdn.net/article/details/146064835Sublime Text 快捷键完全指南https://shuaici.blog.csdn.net/article/details/157148806

相关推荐
用户0874881999172 小时前
Android 资源类型全解析及四大常用布局资源深度指南
android
火锅鸡的味道3 小时前
解决AOSP工程Android Studio打开卡顿
android·python·android studio
2501_915921433 小时前
2026 iOS 上架新趋势 iOS 发布流程模块化
android·ios·小程序·https·uni-app·iphone·webview
毕设源码-钟学长3 小时前
【开题答辩全过程】以 基于Android的高校二手交易系统为例,包含答辩的问题和答案
android
FLEMMINGS3 小时前
当 Android Studio 模拟器提示emulator failed to connect within 5 minutes
android·ide·android studio
鹏程十八少3 小时前
6. Android Shadow与众不同?一文解决插件化四大核心难题:ClassLoader冲突、Activity代理、资源隔离、动态更新(源码分析上)
android·前端·面试
不会写DN3 小时前
PHP mysqli 实用开发指南
android·开发语言·php
studyForMokey3 小时前
【Android面试】Handler专题
android·java·面试
Amctwd3 小时前
【Android】将 html 打包为 apk
android·html·harmonyos