【前言】
CountDownLatch
是JDK提供的一个同步工具,它可以让一个或多个线程挂起等待,一直等到其他线程执行完成才会继续执行。常用方法有countDown
方法和await
方法,CountDownLatch
在初始化时,需要指定一个整数n作为计数器。当调用countDown
方法时,计数器会被减1;当调用await
方法时,如果计数器大于0时,当前线程会被阻塞,一直到计数器被countDown
方法减到0时,当前线程才会继续执行。计数器是无法重置的,当计数器被减到0时,调用await
方法都会直接返回。
一、使用场景:扫描手机内的安装包
由于手机文件夹目录众多,单线程遍历全部文件夹找出所有的安装包会比较费时,但是假如开启多个线程并行去搜索不同的文件夹,然后再将结果汇总起来返回,那么效率就会明显有所提高。然而开启多个线程去搜索是很容易实现,但是由于每个线程耗时不同,有些线程搜索完毕了,但是有些线程可能还在搜索,怎么确保所有的线程都搜索完毕再把结果汇总起来返回呢?这时候借助CountDownLatch
这个类就能轻易地实现
二、场景案例实现
1、开启多线程遍历文件夹,这里使用线程池
去实现,以便达到线程复用的目的
/**
* 扫描手机外存所有目录查找apk文件,需要读取外部权限
*/
public void scanAllApks(Context context) {
//避免多线程同时访问
synchronized (INSTANCE){
// 公共储存下的所有文件
File[] files = Environment.getExternalStorageDirectory().listFiles();
// 私有存储目录下的文件
File filesDir = context.getExternalFilesDir(null);
//多线程并发执行扫描
if (executorService == null) {
executorService = Executors.newFixedThreadPool(10);
}
//遍历
List<Runnable> runnableList = new ArrayList<>();
if (files != null) {
List<File> fileList = new ArrayList<>();
fileList.add(filesDir);
fileList.addAll(Arrays.asList(files));
for (File file : fileList) {
if (file.isDirectory()) {
//当前是目录的话,构造一个Runnale对象,稍后放到线程池去执行
runnableList.add(new SearchApkRunnable(file));
} else {
//当前是文件的话,尝试直接去解析
tryToParseApk(context, file);
}
}
}
//初始化一个计数为文件夹总数的计数器
countDownLatch = new CountDownLatch(runnableList.size());
//使用线程池去执行遍历任务
for(Runnable runnable: runnableList){
executorService.execute(runnable);
}
try {
//当前线程会堵塞在这里,直到所有线程执行完毕才会继续往下执行
countDownLatch.await();
Logger.i("扫描完成");
} catch (InterruptedException e) {
e.printStackTrace();
Logger.e("扫描被打断");
}
}
}
2、每个线程所做的事情就是递归去查找当前目录下的所有安装包,线程执行完毕会调用CountDownLatch
的countDown()
方法将计数器减一文章来源:https://www.toymoban.com/news/detail-637758.html
public class SearchApkRunnable implements Runnable{
private File mFile;
private Context mCxt;
public SearchApkRunnable(Contex context, File file){
this.mFile = file;
this.mCxt = context;
}
@Override
public void run() {
try {
scanDirToFindApk(mCxt, this.mFile);
} catch (Exception e) {
e.printStackTrace();
}finally {
//计数减一
countDownLatch.countDown();
}
}
}
/**
* 扫描目录,查找apk文件
*
* @param dir
*/
private void scanDirToFindApk(Context context, File dir) {
if (Thread.currentThread().isInterrupted()) {
Logger.d("扫描终止:"+ Thread.currentThread().getName());
return;
}
File[] files = dir.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
//递归查找
if (f.isDirectory()) {
//扫描目录
scanDirToFindApk(context,f);
} else {
//是文件的话,尝试解析apk信息
tryToParseApk(context,f);
}
}
}
}
3、查找发现是文件时候,若是apk后缀的文件,尝试解析出apk的信息文章来源地址https://www.toymoban.com/news/detail-637758.html
/**
* 尝试解析APk信息
*
* @param file
* @return
*/
private ApkInfo tryToParseApk(Context context, File file) {
if (file == null) {
return null;
}
String absolutePath = file.getAbsolutePath();
//判断是apk
if (absolutePath.endsWith(".apk")) {
//过滤掉小于0.1M的apk
if (file.length() >= 1024*100) {
PackageInfo packageInfo;
ApkInfo apkInfo;
try {
PackageManager packageManager = context.getPackageManager();
packageInfo = packageManager.getPackageArchiveInfo(absolutePath, PackageManager.GET_ACTIVITIES);
if(packageInfo == null){
return null;
}
//一定要设置sourceDir 与 publicSourceDir,否则获取不到label与icon
apkInfo = createApkInfo(file, packageInfo, packageManager);
//加入安装包集合ArrayList中
APK_INFOS.add(apkInfo);
return apkInfo;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
return null;
}
@NonNull
private ApkInfo createApkInfo(File file, PackageInfo packageInfo, PackageManager packageManager) {
ApkInfo apkInfo;
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if(TextUtils.isEmpty(applicationInfo.sourceDir)){
applicationInfo.sourceDir = file.getAbsolutePath();
applicationInfo.publicSourceDir = file.getAbsolutePath();
}
String appName = applicationInfo.loadLabel(packageManager).toString();
Drawable icon = applicationInfo.loadIcon(packageManager);
String packageName = packageInfo.packageName;
String versionName = packageInfo.versionName;
int versionCode = packageInfo.versionCode;
long apkSize = file.length();
apkInfo = new ApkInfo(appName, packageName, versionCode, versionName, icon, apkSize, file.getAbsolutePath(), file.getName());
return apkInfo;
}
到了这里,关于巧用CountDownLatch实现多线程并行工作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!