在Android中使用CountDownLatch锁定线程

5

我刚开始在我的Android应用程序中尝试使用CountDownLatch。目前,我正在尝试向我的API发出两个Volley请求,并等待数据被检索和存储后再继续线程执行。

这是我的代码示例:

    // new CountDownLatch for 2 requests
    final CountDownLatch allDoneSignal = new CountDownLatch(2);

    transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
        @Override
        public void onSuccess(ArrayList<Contact> resources, String resourceId) {
            transactions.createRealmObject(resources, Contact.class);

            allDoneSignal.countDown();
        }

        @Override
        public void onFail(ArrayList<Contact> resources) {

        }
    });

    transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
        @Override
        public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
            transactions.createRealmObject(resources, Meeting.class);

            allDoneSignal.countDown();
        }

        @Override
        public void onFail(ArrayList<Meeting> resources) {

        }
    });

    try {
        allDoneSignal.await();
        // continue executing code
        // ...
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

问题在于它似乎无法“完成”倒计时,因此会冻结,因为闩锁从未被释放。我已确认API请求正在工作,并且onSuccess回调成功触发,但线程却挂起。
更新:我刚刚注意到,当CountDownLatch设置为0时,它会触发onSuccess,但当我将其设置为大于0的任何值时,它会冻结,并且onSuccess永远不会被调用。看起来线程有些问题。

我可能错了,但是关于线程和值相关的问题,请查看“Volatile variable”概念。 - Pradeep Kumar Kushwaha
应该按预期工作 - 你可以在调用countDown之前记录一些内容,以确保方法真的被调用了吗? - assylias
1
@PradeepKumarKushwaha 这里不需要使用 volatile,latch 已经提供了足够的同步保证。 - assylias
我将尝试使用 Log.wtf 记录这个... - barnacle.m
你还说“为2个请求创建新的CountDownLatch” - 也许要仔细检查allDoneSignal变量在三个地方是否相同(如果代码完全像你在单个方法中发布的那样,那显然是相同的)... - assylias
你能提供整个代码和getResourcesForRealm的代码吗? - Nicolas Filotto
2个回答

6

您的代码存在太多错误,您需要在finally块中调用countDown(),并且还需要在onFail中调用它,否则,在失败的情况下,您的应用程序将会永远被冻结。因此您的代码应该更改为以下内容:

transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
    @Override
    public void onSuccess(ArrayList<Contact> resources, String resourceId) {
        try {
            transactions.createRealmObject(resources, Contact.class);
        } finally {
            allDoneSignal.countDown();
        }
    }

    @Override
    public void onFail(ArrayList<Contact> resources) {
        allDoneSignal.countDown();
    }
});

transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
    @Override
    public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
        try {
            transactions.createRealmObject(resources, Meeting.class);
        } finally {
            allDoneSignal.countDown();
        }
    }

    @Override
    public void onFail(ArrayList<Meeting> resources) {
        allDoneSignal.countDown();
    }
});

虽然这是一般情况下的好建议,但它并没有回答问题。这个问题很糟糕,需要你做出假设。 - Sleiman Jneidi
1
@SleimanJneidi 是的,我知道,但我们都知道CountDownLatch很好用,所以如果程序冻结了,那肯定是因为某种原因countDown()没有按预期调用两次,所以我提供了一种方法来确保即使在失败的情况下也会被调用。 - Nicolas Filotto
感谢良好的错误处理,我已经实现了它,现在正在尝试弄清楚为什么没有触发 countDown() - barnacle.m
@barnacle.m 我认为这意味着 onSuccess 是由调用线程调用的,尝试打印 Thread.currentThread() 的值。 - Nicolas Filotto
我已经尝试将锁作为异步请求的参数传递,并在新线程中进行倒计时,但它仍然无法正常运行。 - barnacle.m

3

抱歉回复晚了,但如果对任何人仍有帮助:

你需要在单独的线程中执行“.await”,因为它会阻塞当前线程。

示例:

final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
    @Override
    public void run() {
        allDoneSignal.await();
        mainThreadHandler.post(new Runnable() {
            doSomethingWhenAllDone();
        });
}
}).start()

你好。这是一个可行的解决方案吗? - sampyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接