盒子
盒子
文章目录
  1. 引言
  2. 高级多线程编程技术
    1. 使用线程池管理线程
    2. 使用Callable和Future获取任务结果
    3. 使用Lock和Condition进行更精细的同步控制
  3. 并发数据结构
    1. ConcurrentHashMap
    2. CopyOnWriteArrayList
    3. BlockingQueue
    4. AtomicInteger
    5. CountDownLatch
    6. CyclicBarrier
  4. 异常处理
  5. Android中的异步任务与HandlerThread
    1. AsyncTask的替代方案
    2. HandlerThread的使用
  6. 性能优化策略
    1. 使用Systrace进行性能分析
    2. 避免UI线程阻塞
    3. 使用轻量级的同步机制
    4. 优化内存管理
  7. 总结
  8. 推荐

深入Android多线程编程与性能优化

引言

在上一篇的入门篇中,我们对Android线程的基础概念和多线程编程模型有了初步了解。本篇将深入探讨多线程编程技术和性能优化策略,以提升应用的效率和响应性。

高级多线程编程技术

使用线程池管理线程

线程池是一组预先创建的线程,用于执行任务。通过使用线程池,可以避免不断创建和销毁线程的开销,提高线程的重用率,同时有效控制并发线程数量。

1
2
3
4
5
6
7
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交任务给线程池执行
executor.submit(() -> {
// 执行任务的代码
});
  • 通过Executors.newFixedThreadPool创建一个包含5个线程的固定大小线程池。
  • executor.submit方法用于将任务提交给线程池执行。

使用Callable和Future获取任务结果

Callable接口允许线程返回结果,而Future接口允许主线程获取线程的执行结果。

1
2
3
4
5
6
7
8
9
Callable<String> callableTask = () -> {
// 执行任务的代码
return "Task completed";
};

Future<String> future = executor.submit(callableTask);

// 获取任务结果
String result = future.get();
  • Callable接口相比Runnable接口,允许在执行完任务后返回一个结果。
  • executor.submit返回一个Future对象,可以通过get方法获取任务执行的结果。

使用Lock和Condition进行更精细的同步控制

synchronized关键字相比,Lock接口提供了更灵活的锁定机制,Condition接口允许线程等待特定条件满足后再继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

// 在线程中使用锁
lock.lock();
try {
// 一些需要同步的代码块
condition.await(); // 等待条件满足
} finally {
lock.unlock();
}

// 在另一个线程中发出信号
lock.lock();
try {
condition.signal(); // 发送信号
} finally {
lock.unlock();
}
  • ReentrantLockLock接口的一种实现,提供了可重入的锁。
  • condition.await()用于让线程等待,condition.signal()用于唤醒等待的线程。

并发数据结构

并发数据结构是在多线程编程中至关重要的一部分。并发数据结构提供了在多线程环境下安全访问和修改数据的机制,以确保线程安全性和避免竞态条件。

以下是一些常见的并发数据结构及其应用

ConcurrentHashMap

ConcurrentHashMapHashMap 的线程安全版本,它允许在不同的部分上并发地读写数据,提高了并发性能。在多线程环境中,使用 ConcurrentHashMap 可以避免对整个数据结构的锁定,从而提高并发性。

1
2
3
4
5
6
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();

concurrentMap.put("key1", 1);
concurrentMap.put("key2", 2);

int value = concurrentMap.get("key1");

CopyOnWriteArrayList

CopyOnWriteArrayListArrayList 的线程安全版本,它通过在修改操作时创建底层数组的副本来保证线程安全。它适用于读多写少的场景。

1
2
3
4
5
6
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

copyOnWriteArrayList.add("Item1");
copyOnWriteArrayList.add("Item2");

String item = copyOnWriteArrayList.get(0);

BlockingQueue

BlockingQueue 是一个阻塞队列,它提供了在队列为空或已满时阻塞线程的操作。这在生产者-消费者模型中特别有用。

1
2
3
4
5
6
7
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(10);

// 生产者线程
blockingQueue.put("Item");

// 消费者线程
String item = blockingQueue.take();

AtomicInteger

AtomicInteger 是一个原子整数,它提供了一组原子操作,可确保在多线程环境中进行递增、递减等操作时的线程安全性。

1
2
3
AtomicInteger atomicInteger = new AtomicInteger(0);

int result = atomicInteger.incrementAndGet(); // 原子递增操作

CountDownLatch

CountDownLatch 是一个同步工具类,它允许一个或多个线程等待其他线程完成操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CountDownLatch latch = new CountDownLatch(2);

// 线程1
new Thread(() -> {
// 执行某些操作
latch.countDown();
}).start();

// 线程2
new Thread(() -> {
// 执行某些操作
latch.countDown();
}).start();

// 主线程等待两个线程完成
latch.await();

CyclicBarrier

CyclicBarrier 是另一个同步工具类,它允许一组线程相互等待,直到所有线程都到达某个屏障点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

// 线程1
new Thread(() -> {
// 执行某些操作
cyclicBarrier.await();
}).start();

// 线程2
new Thread(() -> {
// 执行某些操作
cyclicBarrier.await();
}).start();

// 线程3
new Thread(() -> {
// 执行某些操作
cyclicBarrier.await();
}).start();

// 主线程等待所有线程到达屏障点
cyclicBarrier.await();

这些并发数据结构为多线程编程提供了有力的支持,但在使用它们时需要谨慎,以确保正确地处理并发访问和修改。在合适的场景下,合理使用这些数据结构可以提高程序的性能和并发度。

异常处理

UncaughtExceptionHandler 接口可以用于捕获线程中未处理的异常。通过设置线程的 UncaughtExceptionHandler,可以在异常发生时执行自定义的处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UncaughtExceptionHandlerExample {

public static void main(String[] args) {
// 创建一个线程
Thread thread = new Thread(() -> {
// 模拟发生异常
throw new RuntimeException("Simulated exception");
});

// 设置线程的UncaughtExceptionHandler
thread.setUncaughtExceptionHandler((t, e) ->
System.out.println("Uncaught exception in thread " + t.getName() + ": " + e.getMessage()));

// 启动线程
thread.start();
}
}

在这个例子中,我们创建了一个线程,并设置了它的 UncaughtExceptionHandler。当线程中发生未捕获的异常时,UncaughtExceptionHandleruncaughtException 方法将被调用,我们在这里简单地输出了异常信息。

请注意,这种机制对于捕获主线程的异常可能不太适用。对于主线程的异常处理,更好的方式是使用 Thread.setDefaultUncaughtExceptionHandler 来设置默认的异常处理器。

1
2
Thread.setDefaultUncaughtExceptionHandler((t, e) ->
System.out.println("Uncaught exception in thread " + t.getName() + ": " + e.getMessage()));

这将影响所有线程,除非它们显式地设置了自己的 UncaughtExceptionHandler

Android中的异步任务与HandlerThread

AsyncTask的替代方案

由于AsyncTask已被废弃,推荐使用ExecutorHandler的结合,或者使用Kotlin协程(在入门篇中已有介绍)来执行异步任务。

1
2
3
4
5
6
// 使用Executor执行异步任务
Executor executor = Executors.newSingleThreadExecutor();

executor.execute(() -> {
// 执行异步任务的代码
});

HandlerThread的使用

HandlerThread是Android中的一个辅助类,它封装了与Looper相关的操作,使得在后台线程中执行任务更为方便。

1
2
3
4
5
6
7
8
9
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();

Handler handler = new Handler(handlerThread.getLooper());

// 在后台线程执行任务
handler.post(() -> {
// 执行任务的代码
});

性能优化策略

使用Systrace进行性能分析

Systrace是Android系统提供的一种性能分析工具,可以用来检查应用中的性能瓶颈,找到耗时操作,优化线程执行时间。

  • 通过Android Studio中的Profiler工具,选择Systrace进行性能分析。

避免UI线程阻塞

将耗时任务转移到后台线程执行,确保UI线程不会因为耗时操作而阻塞,提高应用的响应性。

1
2
3
4
5
6
// 使用Handler将任务提交到后台线程执行
Handler handler = new Handler(Looper.getMainLooper());

handler.post(() -> {
// 执行耗时任务
});

使用轻量级的同步机制

避免过多使用重量级的同步机制,如synchronized关键字,可以选择使用java.util.concurrent包中的更轻量级的机制,例如ReentrantLock

优化内存管理

及时释放不再需要的对象,避免内存泄漏。可以使用工具如LeakCanary来帮助检测内存泄漏问题。

1
2
3
// 使用WeakReference来持有对象的弱引用,有助于及时释放不再需要的对象
WeakReference<MyObject> weakReference = new WeakReference<>(myObject);
myObject = null; // 释放强引用

总结

我们深入了解了Android中更高级的多线程编程技术,包括线程池的使用、锁与条件的灵活应用、并发数据结构以及一些性能优化的策略。希望读者能够在实际项目中灵活运用这些知识,构建高效稳定的Android应用。

推荐

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App Startup的全部功能,还提供额外的同步与异步等待、线程控制与多进程支持等功能。

AwesomeGithub: 基于Github的客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于JetPack\&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。

支持一下
赞赏是一门艺术