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

Android线程等待失败求助:页面切换时线程异常或应用卡顿

多页面Socket消息接收线程的等待状态问题

问题场景

在A、B两个页面分别启动了消息接收线程,希望从A切换到B时让A的线程进入等待状态,避免两个线程竞争同一条消息,但实现过程中遇到了问题。

核心代码

接收线程的实现:

// BufferedReader brClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
private class receiveMsg implements Runnable {
    @Override
    public void run() {
        try {
            String data;
            while ((data = brClient.readLine()) != null) {
                // 对应A/B页面的不同消息处理逻辑
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

异常情况

在A页面的onPause方法中调用receiveMsgThread.wait()时,抛出如下异常:

java.lang.RuntimeException: Unable to pause activity {com.example.chat/com.example.chat.B_Container_Activity}: java.lang.IllegalMonitorStateException: object not locked by thread before wait()

添加同步块后的卡顿问题

参考方案添加synchronized块后,异常消失,但从A切换到B时应用卡顿,相关日志:

I/System.out: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> onPause
I/ZrHungImpl: sendRawEvent. wpId = 257
E/HiView.HiEvent: length is 0 or exceed MAX: 1024
I/om.example.cha: Thread[7,tid=21291,WaitingInMainSignalCatcherLoop,Thread*=0x7b849a5c00,peer=0x16644030,"Signal Catcher"]: reacting to signal 3
I/om.example.cha: 
I/om.example.cha: Wrote stack traces to tombstoned
I/ZrHungImpl: sendRawEvent. wpId = 258
E/HiView.HiEvent: length is 0 or exceed MAX: 1024

解决方案

问题本质

  1. 调用wait()必须先持有该对象的锁,否则会抛出IllegalMonitorStateException,这是首次报错的原因。
  2. 添加同步块后卡顿,是因为在**主线程(onPause由主线程执行)**中调用wait(),导致主线程被挂起,UI直接失去响应。

正确实现:用开关变量控制线程状态

不要直接调用线程的wait(),改用volatile标志位配合锁来控制线程暂停/恢复,避免主线程阻塞:

修改后的接收线程代码

private BufferedReader brClient;

private class receiveMsg implements Runnable {
    // volatile保证多线程间的状态可见性
    private volatile boolean isPaused = false;
    // 专用锁对象,避免用Thread本身作为锁
    private final Object pauseLock = new Object();

    // 外部调用此方法暂停线程
    public void pauseThread() {
        isPaused = true;
    }

    // 外部调用此方法恢复线程
    public void resumeThread() {
        synchronized (pauseLock) {
            isPaused = false;
            pauseLock.notifyAll(); // 唤醒等待中的线程
        }
    }

    @Override
    public void run() {
        try {
            String data;
            while ((data = brClient.readLine()) != null) {
                // 检查是否需要暂停,用while循环防止虚假唤醒
                synchronized (pauseLock) {
                    while (isPaused) {
                        pauseLock.wait(); // 此处wait会释放锁,不会阻塞主线程
                    }
                }
                // 执行对应页面的消息处理逻辑
                // different operations to fit page A and B
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

页面生命周期中调用控制方法

在A页面的onPause和onResume中调用线程的控制方法:

private receiveMsg aReceiveThread;

@Override
protected void onPause() {
    super.onPause();
    if (aReceiveThread != null) {
        aReceiveThread.pauseThread();
    }
}

@Override
protected void onResume() {
    super.onResume();
    if (aReceiveThread != null) {
        aReceiveThread.resumeThread();
    }
}

额外优化建议

  • 避免多线程共用同一输入流:如果A、B线程共用同一个brClient,即使暂停A线程,B线程的readLine()依然会读取缓冲区数据,可能导致消息丢失。更合理的方案是只启动一个全局消息接收线程,将消息分发给当前活跃的页面。
  • 页面销毁时清理资源:在页面onDestroy中关闭线程和Socket,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:40:35