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

Semaphore阻塞主线程无法释放求助:获取AccessToken后仍阻塞

主线程阻塞未释放的问题排查

嘿,我来帮你梳理下这个问题——你在MainActivity的onCreate里调用waitUntilAccessTokenIsObtained.acquire()阻塞主线程,异步获取Token后调用release()却没唤醒主线程,大概率是这几个细节没处理到位:

1. 同步工具类的初始化或类型问题

  • 如果你用的是CountDownLatch:初始化时是不是设置了大于1的计数?比如new CountDownLatch(2),那只调用一次countDown()肯定没法唤醒主线程,必须确保计数是1,一次countDown()就能归零。
  • 如果你用的是Semaphore:初始化时是不是给了0个许可?new Semaphore(0),这时候release()是增加许可,acquire()是获取许可,但如果异步请求里的release()没执行到,主线程就会一直阻塞。

2. 异步请求的release()是否真的执行了

  • 检查getAccessToken()的回调逻辑:是不是只在请求成功时才调用release()?如果请求超时、失败或者抛出异常,有没有兜底执行release()?比如只在onSuccess里调用,那请求失败时主线程就永远卡着了。
  • 确认异步请求是否在非主线程执行:如果getAccessToken()实际上是在主线程同步执行的,那acquire()已经把主线程卡死,release()根本没机会运行。

3. 主线程阻塞的生命周期风险

  • 在onCreate里直接阻塞主线程很容易触发ANR(应用无响应),就算后面release()执行了,系统可能已经因为主线程超时把进程杀掉了。建议给acquire()/await()加上超时时间,比如await(5, TimeUnit.SECONDS),避免无限阻塞。

4. 同步工具类的实例是否一致

  • 有没有可能waitUntilAccessTokenIsObtained是多个实例?比如异步请求里调用的是另一个对象的release(),而主线程acquire()的是另一个实例,这种情况下肯定无法触发唤醒,要确保是同一个对象。

正确示例代码(用CountDownLatch)

public class MainActivity extends AppCompatActivity {
    private CountDownLatch waitForTokenLatch = new CountDownLatch(1);
    private String currentToken;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 启动异步请求获取Token
        fetchAccessToken();

        try {
            // 最多等待5秒,防止ANR
            waitForTokenLatch.await(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 安全加载资源(如果新Token没拿到就用旧Token)
        loadAppResources(currentToken != null ? currentToken : getOldToken());
    }

    private void fetchAccessToken() {
        // 模拟异步网络请求,实际项目可以用Retrofit/OkHttp
        new Thread(() -> {
            try {
                // 模拟网络耗时
                Thread.sleep(1500);
                currentToken = "fresh_access_token_123";
            } catch (Exception e) {
                e.printStackTrace();
                // 请求失败,用旧Token兜底
                currentToken = getOldToken();
            } finally {
                // 无论成功失败都必须释放,避免主线程阻塞
                waitForTokenLatch.countDown();
            }
        }).start();
    }

    private String getOldToken() {
        // 从SharedPreferences等地方获取旧Token
        return "old_valid_token_456";
    }

    private void loadAppResources(String token) {
        // 加载资源的业务逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:39:18