对于在Android开发中进行多线程编程来说,线程同步是一个需要经常面对的问题。例如主线程创建了几个子线程来执行复杂的计算,要求所有的子线程执行完后返回结果给主线程,主线程才能继续后续的操作,此时就需要考虑线程同步了。我也是从阅读Android源码中关于相机的部分后才发现有两种方式可以比较容易的实现线程同步,下面一一讲解。
1.采用Thread.join()函数
在当前线程中调用thread.join()会导致当前线程阻塞,此时只有当thread线程执行完后,当前线程才能继续往下执行。
package com.android.test;
import java.util.concurrent.CountDownLatch;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class ThreadJoinTest extends Activity {
CountDownLatch mCountDownLatch = new CountDownLatch(1);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for(int i = 0; i < 5; i++){
Log.i("com", "thread i = " + i);
Thread.sleep(500);
}
} catch (Exception e) {
// TODO: handle exception
}
}
});
thread.start();
Log.i("com", "main start");
try {
thread.join();
//thread.join(1500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
Log.i("com", "main end ");
}
}
程序运行后log如下:
06-22 21:40:07.953: INFO/com(17958): main start
06-22 21:40:07.953: INFO/com(17958): thread i = 0
06-22 21:40:08.453: INFO/com(17958): thread i = 1
06-22 21:40:08.953: INFO/com(17958): thread i = 2
06-22 21:40:09.453: INFO/com(17958): thread i = 3
06-22 21:40:09.953: INFO/com(17958): thread i = 4
06-22 21:40:10.453: INFO/com(17958): main end
从log可以看出,程序执行后,首先主线程打印了"main start",执行到thread.join()后,被阻塞了,此时子线程开始打印log,而且时间间隔都是500毫秒,当子线程执行完后,主线程才最终打印出"main
end"。
有些情况下子线程因为某种原因也可能被阻塞,则此时主线程可能永远得不到继续执行,所以我们可以采用thread.join()的一个重载方法join(long millis),其可以传入一个时间参数,表示等到thread线程执行完或者超过mills时间后才能允许调用这个函数的线程继续往下执行,例如把刚才代码中的join()换成thread.join(1500),则程序运行后log如下:
06-22 22:12:17.183: INFO/com(19691): main start
06-22 22:12:17.183: INFO/com(19691): thread i = 0
06-22 22:12:17.683: INFO/com(19691): thread i = 1
06-22 22:12:18.183: INFO/com(19691): thread i = 2
06-22 22:12:18.683: INFO/com(19691): main end
06-22 22:12:18.683: INFO/com(19691): thread i = 3
06-22 22:12:19.183: INFO/com(19691): thread i = 4
由于子线程执行完的时间超过1500毫秒,所以在thread.join(1500)执行完并等待了1500毫秒后,主线程开始再次执行,注意此时子线程并没有停止,而是跟主线程同时执行。
2.采用CountDownLatch
CountDownLatch内部维护一个计数器,初始化的时候会传入一个整型值作为计数器的初始值,其主要包括两个函数,await()和countDown(),当在某个线程中调用了await(),则该线程被阻塞,每调用countDown()则计数器的值减1,只有当计数器为0时刚才被阻塞的线程才能继续执行,这里修改官方上的demo来说明情况。
package com.android.test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class CountDownLatchTest extends Activity {
private final int N = 5;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i){ // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
}
Log.i("com", "main thread start");
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Log.i("com", "main thread doing");
startSignal.countDown(); // let all threads proceed
try {
//doneSignal.await(2,TimeUnit.SECONDS);
doneSignal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("com", "main thread finish");
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
Log.i("com", "Sub Thread start");
//startSignal.await(2,TimeUnit.SECONDS);
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
}
void doWork() {
Log.i("com", "Sub Thread doing");
try {
Thread.sleep(2000);
Log.i("com", "Sub Thread end");
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
程序打印log如下:
06-23 09:04:46.863: INFO/com(28948): Sub Thread start
06-23 09:04:46.863: INFO/com(28948): Sub Thread start
06-23 09:04:46.863: INFO/com(28948): Sub Thread start
06-23 09:04:46.863: INFO/com(28948): Sub Thread start
06-23 09:04:46.863: INFO/com(28948): main thread start
06-23 09:04:46.863: INFO/com(28948): Sub Thread start
06-23 09:04:48.863: INFO/com(28948): main thread doing
06-23 09:04:48.863: INFO/com(28948): Sub Thread doing
06-23 09:04:48.863: INFO/com(28948): Sub Thread doing
06-23 09:04:48.863: INFO/com(28948): Sub Thread doing
06-23 09:04:48.863: INFO/com(28948): Sub Thread doing
06-23 09:04:48.863: INFO/com(28948): Sub Thread doing
06-23 09:04:50.863: INFO/com(28948): Sub Thread end
06-23 09:04:50.863: INFO/com(28948): Sub Thread end
06-23 09:04:50.863: INFO/com(28948): Sub Thread end
06-23 09:04:50.863: INFO/com(28948): Sub Thread end
06-23 09:04:50.863: INFO/com(28948): Sub Thread end
06-23 09:04:50.863: INFO/com(28948): main thread finish
在代码中,尽管所有子线程在创建后立马就启动了,并且主线程调用了Thread.sleep(2000)阻塞自己2秒,可是子线程的log还是在main
thread doing之后才开始打印,这正是由于在每个子线程内部都调用了startSignal.await(),所以需要等待主线程调用完startSignal.countDown()才能继续后面的操作。之后主线程中也调用了doneSignal.await(),而doneSignal所设置的计数器为5,所以这时又得等到所有子线程都执行完doneSignal.countDown()后才能打印出main thread finish。这样就达到了主线程和子线程的相互同步。当然await()也有另一个重载函数await
(long timeout, TimeUnit unit),其timeout类似join(),不过要比join好的一点是它可以传入一个时间单位TimeUnit,设置时间更灵活点,具体可以参见sdk。
分享到:
相关推荐
利用 CountDownLatch 类实现线程同步,而不用回调机制。详见我的博文 http://blog.csdn.net/kroclin/article/details/37956949
CountDownLatch与thread.join()的区别
mybaits 多线程 实现数据批量插入 (运用CountDownLatch实现闭锁) 1、mybatis批处理 2、数据分批量查询 3、数据分批量插入
3. 如何在 Java 中实现线程? 4. 用 Runnable 还是 Thread? 5. Thread 类中的 start () 和 run () 方法有什么区别? 6. Java 中 Runnable 和 Callable 有什么不同? 7. Java 中 CyclicBarrier 和 CountDownLatch 有...
线程 ...................................................................................................................................................... 20 2.2. JVM 内存区域 .........................
主要介绍了Java中CountDownLatch进行多线程同步详解及实例代码的相关资料,需要的朋友可以参考下
2.1. 线程 ...................................................................................................................................................... 20 2.2. JVM 内存区域 .....................
NULL 博文链接:https://cpjsjxy.iteye.com/blog/2272451
主要介绍了如何使用CountDownLatch同步java多线程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
尚硅谷_JUC线程高级_CountDownLatch 闭锁 ·6. 实现 Callable 接口 ·7. 尚硅谷_JUC线程高级_同步锁 Lock ·8. 尚硅谷_JUC线程高级_生产者消费者案例-虚假唤醒 ·9. 尚硅谷_JUC线程高级_Condition 线程通信 ·10. ...
主要为大家详细介绍了使用CountDownLatch等待多线程全部执行完成,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
java.util.concurrent.CountDownLatch通过org.redisson.core.RTopic 实现分布式订阅/发布通过 org.redisson.core.RHyperLogLog 实现分布式HyperLogLog 线程安全支持 OSGi超过110个单元测试 标签:Redisson...
第一章 线程基础、线程之间的共享和协作 3 一、基础概念 3 1. 什么是进程和线程 3 2. CPU核心数和线程数的关系 3 3. 澄清并行和并发 5 4. 多线程程序需要注意事项 6 二、认识Java里的线程 7 1. Java程序天生就是多...
线程之间通信之join应用与实现原理剖析.mp4 ThreadLocal 使用及实现原理.mp4 并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 ...
具有countDownLatch的线程同步模式
浅析Java中CountDownLatch用法 CountDownLatch如其所写,是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。下面以一个模拟运动员比赛的例子加以说明。 ...
CountDownLatch是一个同步工具类,它通过一个计数器来实现的,初始值为线程的数量。每当一个线程完成了自己的任务,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复...
同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环栅栏以及他们的实例来介绍Java并发程序中的同步控制。 目录线程安全 Thread Safety重入锁 ReentrantLock读写锁 ...
CountDownLatch是什么? CountDownLatch是在java1.5被引入的,跟它一起被引入的并发...CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当
其实现依赖于AQS(可以参考抽象队列式同步器AQS详解) 具体来说一个经典得应用案例是,主线程等待子线程执行完毕,再进行信息汇总,退出主函数。 如下代码所示。我们可以大胆猜测其初始化构造,赋值计数器值,之后,...