「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」
Android中的并发问题
有一个问题我思考了很久,目前心里也并没有最合适的解决方案,那就是安卓的并发问题,遇到并发问题该怎么办呢? 本篇文章的目的是为了优化并发的问题。
并发
在多年的安卓编程生涯中,并发其实遇到的比较少,主要大家还是和界面打交道比较多,然后就是安卓的蓝牙、相机这之类的硬件功能。
我总结了我遇到的大量的并发集中于下面的两个场景:
- 突然接收到大量的网络数据,或者大量的数据需要网络传输
- 大量数据需要写入数据库
在我看来,超出设备处理速度的大量数据就只能排队。 就像大海里水一下子冲入一条河流,这谁顶得住...
需求
当大量数据来临时,要有序的进行处理,尽可能快的处理。
例子验证
首先模拟一个任务对象,它有索引和任务是否完成的属性。
public static class TaskBean {
public int index;
public boolean isDone;
public TaskBean(int index, boolean isDone) {
this.index = index;
this.isDone = isDone;
}
}
接着添加100个任务,模拟并发的场景。
mTaskBeanList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
mTaskBeanList.add(new TaskBean(i, false));
}
准备工作做好了,重点代码来了,我们假设一个任务需要100ms时间去执行,所以正常情况下得到下面的代码。
private void test1() {
long time = System.currentTimeMillis();
Log.d(TAG, "start");
for (TaskBean taskBean : mTaskBeanList) {
try {
Thread.sleep(100);
taskBean.isDone = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, "end 耗时:" + (System.currentTimeMillis() - time));
// end 耗时:10074
}
这段代码耗时 10074ms,也就是所有的任务都是按顺序,一个一个执行。
把这些代码放在线程里会怎样。
int index = 0;
private void test2() {
Object lock = new Object();
long time = System.currentTimeMillis();
Log.d(TAG, "start");
for (TaskBean taskBean : mTaskBeanList) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
taskBean.isDone = true;
synchronized (lock) {
++index;
Log.d(TAG, "index:" + index);
if (index == 100)
Log.d(TAG, "end: 耗时:" + (System.currentTimeMillis() - time));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
// 耗时:127
当我们用很多子线程时,耗时明显减少,即使为了同步数量加上了同步锁,耗时127ms仍然大大减少了开销时间。 这样频繁创建和回收线程当然不好,下面试试线程池的操作。
int index = 0;
private void test3() {
Object lock = new Object();
long time = System.currentTimeMillis();
Log.d(TAG, "start");
for (TaskBean taskBean : mTaskBeanList) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
taskBean.isDone = true;
synchronized (lock) {
++index;
Log.d(TAG, "index:" + index);
if (index == 100)
Log.d(TAG, "end: 耗时:" + (System.currentTimeMillis() - time));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
耗时: 2525毫秒
为什么使用线程池之后,花费的时间反而变长了呢,这是因为在创建线程池的过程中,可以指定核心线程数量、最大线程数量,因为我们还需要保护app其它部分的正常运行。