Android中的并发问题

800 阅读2分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

Android中的并发问题

有一个问题我思考了很久,目前心里也并没有最合适的解决方案,那就是安卓的并发问题,遇到并发问题该怎么办呢? 本篇文章的目的是为了优化并发的问题。

并发

在多年的安卓编程生涯中,并发其实遇到的比较少,主要大家还是和界面打交道比较多,然后就是安卓的蓝牙、相机这之类的硬件功能。
我总结了我遇到的大量的并发集中于下面的两个场景:

  1. 突然接收到大量的网络数据,或者大量的数据需要网络传输
  2. 大量数据需要写入数据库

在我看来,超出设备处理速度的大量数据就只能排队。 就像大海里水一下子冲入一条河流,这谁顶得住...

需求

当大量数据来临时,要有序的进行处理,尽可能快的处理。

例子验证

首先模拟一个任务对象,它有索引和任务是否完成的属性。

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其它部分的正常运行。