主线程如何在子线程完成之后再完成

本文介绍了Java中实现主线程等待所有子线程执行完毕的方法,包括使用Thread的join()方法、CountDownLatch以及CyclicBarrier等并发工具类,并对比了CountDownLatch与CyclicBarrier的区别。

java主线程等待所有子线程执行完毕在执行,这个需求其实我们在工作中经常会用到,比如用户下单一个产品,后台会做一系列的处理,为了提高效率,每个处理都可以用一个线程来执行,所有处理完成了之后才会返回给用户下单成功,下面就说一下我能想到的方法,欢迎大家批评指正:

  1. 用sleep方法,让主线程睡眠一段时间,当然这个睡眠时间是主观的时间,是我们自己定的,这个方法不推荐,但是在这里还是写一下,毕竟是解决方法

java主线程等待所有子线程执行完毕在执行(常见面试题)


2.使用Thread的join()等待所有的子线程执行完毕,主线程在执行,thread.join()把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

java主线程等待所有子线程执行完毕在执行(常见面试题)


下面结合这个问题我介绍一些并发包里非常有用的并发工具类

3.等待多线程完成的CountDownLatch

java主线程等待所有子线程执行完毕在执行(常见面试题)


在这里说明一点,countDownLatch不可能重新初始化或者修改CountDownLatch对象内部计数器的值,一个线程调用countdown方法happen-before另外一个线程调用await方法

4.同步屏障CyclicBarrier

java主线程等待所有子线程执行完毕在执行(常见面试题)


写到这里大家不免有些疑问,countDownLatch和cyclicBarrier有什么区别呢,他们的区别:countDownLatch只能使用一次,而CyclicBarrier方法可以使用reset()方法重置,所以CyclicBarrier方法可以能处理更为复杂的业务场景。

我曾经在网上看到一个关于countDownLatch和cyclicBarrier的形象比喻,就是在百米赛跑的比赛中若使用 countDownLatch的话冲过终点线一个人就给评委发送一个人的成绩,10个人比赛发送10次,如果用CyclicBarrier,则只在最后一个人冲过终点线的时候发送所有人的数据,仅仅发送一次,这就是区别。

<think> 我们面对的问题是:在使用context的runOnUiThread(或其他post方法)将任务提交到主线程后,如何让子线程等待主线程完成该任务后再继续执行? 根据引用内容,我们知道有几种方法可以在子线程中更新UI(即向主线程提交任务): 1. Activity.runOnUiThread(Runnable) 2. View.post(Runnable) 3. Handler(特别是关联到主线程Looper的Handler) 但是,这些方法都是异步的:子线程提交任务后不会等待主线程执行完毕,而是继续执行。如果子线程需要等待主线程完成更新后再做某些操作,我们需要额外的同步机制。 解决方案:我们可以使用同步工具,例如CountDownLatch或者Handler+Message,来实现子线程等待主线程完成。 这里以CountDownLatch为例,因为它相对简单: 步骤: 1. 在子线程中创建一个CountDownLatch,计数为1。 2. 将需要更新UI的任务包装成一个Runnable,在这个Runnable中执行UI更新,并在最后调用latch.countDown()。 3. 子线程在提交这个Runnable之后,调用latch.await(),这样就会阻塞子线程直到主线程执行完任务并调用countDown()。 注意:由于我们是在子线程中等待,所以必须确保这个等待不会导致主线程死锁(一般不会,因为主线程仍在运行)。另外,要小心不要在UI线程中调用await,否则可能导致ANR。 示例代码: 假设我们在子线程中,并且有一个Activity的context(需要确保context是Activity类型,且是主线程的Activity,因为runOnUiThread是Activity的方法)。 ```java // 在子线程中 final CountDownLatch latch = new CountDownLatch(1); // 获取Activity(确保context是Activity类型) Activity activity = (Activity) context; activity.runOnUiThread(new Runnable() { @Override public void run() { try { // 在这里更新UI // 例如:textView.setText("更新完成"); } finally { // 确保无论是否异常,都会减少计数 latch.countDown(); } } }); try { // 等待主线程完成UI更新 latch.await(); // 此时UI更新已完成,可以继续子线程的工作 } catch (InterruptedException e) { e.printStackTrace(); } ``` 如果使用View.post(Runnable)方法,同样可以: ```java CountDownLatch latch = new CountDownLatch(1); view.post(new Runnable() { @Override public void run() { try { // 更新UI } finally { latch.countDown(); } } }); latch.await(); // 在子线程中等待 ``` 如果使用Handler(关联到主线程的Looper): ```java Handler mainHandler = new Handler(Looper.getMainLooper()); CountDownLatch latch = new CountDownLatch(1); mainHandler.post(new Runnable() { @Override public void run() { try { // 更新UI } finally { latch.countDown(); } } }); latch.await(); ``` 注意事项: 1. 在子线程中调用await()会阻塞子线程,直到主线程完成UI更新并调用countDown()。 2. 避免在主线程中使用await(),因为这会阻塞主线程,导致ANR。 3. 确保在UI任务中调用countDown(),即使在发生异常的情况下也要调用(所以放在finally块中)。 另外,也可以考虑使用其他同步机制,如Semaphore、CyclicBarrier等,但CountDownLatch在这里是最简单的。 总结:使用CountDownLatch可以实现在子线程中提交UI更新任务并等待其完成。 §§相关问题§§ 1. 在Android中,除了CountDownLatch,还有哪些方法可以实现线程间同步? 2. 在子线程中更新UI有哪些注意事项? 3. 如何避免在Android中使用线程时发生死锁? </think> 在 Android 开发中,当通过 `context.post()` 或 `runOnUiThread()` 将任务提交到主线程后,如果需要**让子线程等待主线程完成该任务**,可以使用 `CountDownLatch` 或 `Handler` 实现同步。以下是具体方法: --- ### 方法一:使用 `CountDownLatch`(推荐) `CountDownLatch` 是 Java 提供的同步工具,可让子线程等待主线程任务完成: ```java // 在子线程中 final CountDownLatch latch = new CountDownLatch(1); // 初始化计数器为1 // 提交任务到主线程 ((Activity) context).runOnUiThread(new Runnable() { @Override public void run() { try { // 执行主线程任务(如更新UI) textView.setText("更新完成"); } finally { latch.countDown(); // 任务完成,计数器减1(确保执行) } } }); try { latch.await(); // 子线程阻塞,直到主线程任务完成 // 此处继续执行子线程后续逻辑 } catch (InterruptedException e) { e.printStackTrace(); } ``` --- ### 方法二:使用 `Handler` + 自定义回调 通过 `Handler` 向主线程发送消息,并在任务完成后通知子线程: ```java // 1. 在主线程创建Handler Handler mainHandler = new Handler(Looper.getMainLooper()); // 2. 在子线程中 final Object lock = new Object(); // 同步锁 mainHandler.post(new Runnable() { @Override public void run() { // 执行主线程任务 textView.setText("更新完成"); // 通知子线程任务完成 synchronized (lock) { lock.notifyAll(); } } }); // 子线程等待 synchronized (lock) { try { lock.wait(); // 子线程阻塞,等待主线程通知 } catch (InterruptedException e) { e.printStackTrace(); } } // 此处继续执行子线程后续逻辑 ``` --- ### 关键注意事项 1. **线程安全**: - `CountDownLatch.await()` 会阻塞子线程,确保在非主线程中使用。 - 避免在主线程调用 `await()` 或 `wait()`,否则会导致 ANR。 2. **Context 有效性**: - 强转 `(Activity) context` 前需确认 `context` 是 `Activity` 实例(避免 `ClassCastException`)。 - 在 `Activity` 销毁时取消同步,防止内存泄漏。 3. **替代方案**: - 若任务无严格顺序要求,可用 `LiveData` 或 `RxJava` 简化线程切换。 - 对于 `View.post(Runnable r)`,同样适用 `CountDownLatch` 同步。 --- **总结**: 通过 `CountDownLatch` 或 `Handler` 的同步机制,可实现子线程等待主线程任务完成后再继续执行。优先推荐 `CountDownLatch`,其代码简洁且不易出错。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值