Concurrency

在java.util.concurrent.atomic和java.util.concurrent.locks包中定义了更多的类。java.util.concurrent.atomic包包含如下的类:
(1) AtomicBoolean
(2) AtomicInteger
(3) AtomicIntegerArray
(4) AtomicIntegerFieldUpdater(abstract)
(5) AtomicLong
(6) AtomicLongArray
(7) AtomicLongFieldUpdater(abstract)
(8) AtomicMarkableReference
(9) AtomicReference
(10) AtomicReferenceArray
(11) AtomicReferenceFieldUpdater(abstract)
(12) AtomicStampedReference

大多数类需要简单的解释,因为他们只是简单的定义了方法去自动更新值。比如,AtomicInteger类定义了addAndGet()方法,增加一个给定的值到AtomicInteger的当前值,返回更新后的值。在这个包中定义的抽象类是内部使用的,很少在你的应用中直接使用到。
除了java.util.concurrent包中的CountDownLatch, CyclicBarrier和Semaphore类,更多的同步方法定义在java.util.concurrent.locks包中:
(1) AbstractOwnableSynchronizer(abstract, 从API 5开始)
(2) AbstractQueuedLongSynchronizer(abstract, API 9)
(3) AbstractQueuedLongSynchronizer(API 9)
(4) AbstractQueuedSynchronizer(abstract)
(5) AbstractQueuedSynchronizer.ConditionObject
(6) LockSupport
(7) ReentrantLock
(8) ReentrantReadWriteLock
(9) ReentrantReadWriteLock.ReadLock
(10) ReentrantReadWriteLock.WriteLock

这些类通常不会用在Android应用中。可能你用的最多的是ReentrantReadWriteLock类,和它的ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WirteLock对,因为他们允许多个读线程去访问数据(只要没有写线程修改数据),写线程只能同时存在一个。这是一个通用的对象,当多个线程访问同一个用来读的数据,你期望去最大化吞吐量。
作为一个通用规则,在线程间共享数据添加了问题(吞吐量,并发问题)。同步问题可以非常复杂,成功的共享数据,你需要对问题有一个很好的理解。同步相关的调试问题是一个努力,所以你需要在尝试优化吞吐量之前简化。在任何优化开始之前集中于你的应用质量。

多核

最近出现了很多基于多核架构的安卓设备。比如,Samsung Galaxy Tab 10.1和Motorola Xoom平板使用双核处理器(Cortex A9 core)。一个多核处理器,可以同时执行多线程,不像一个单核处理器。也就是说,很简单可以看到这将如何提升性能的,因为一个双核处理器理论上可以做的是单核的两倍(其他的都是相同的情况下,比如,时钟周期)。尽管针对多核的优化不像听起来那么简单,而且存在一些警告,你的应用可以明显的看到多核处理器带来的额外的能力。双核的CPU的设备包括:
(1) Samsung Galaxy Tab 10.1
(2) Motorola Xoom
(3) Motorola Phonton 4G
(4) Motorola Droid 3
(5) HTC EVO 3D
(6) LG OPtimus 2X
(7) Samsung Galaxy Nexus

很多情况下,你不需要关心设备有多少个核。使用Thread对象或者AsyncTask委派特定的操作到一个单独的线程通常是足够的,你仍然可以在单核的处理器上创建多线程。如果处理器有几个核,线程将简单的运行在不同的处理器单元上,对你的应用来说是透明的。
也就是说,你真正的需要是使大多数的CPU去得到一个可接受的性能级别,设计算法特别是针对多核。
为了达到最好的性能,你的应用首先需要找出多少核是可用的,通过简单的调用RunTime.availableProcessors()方法,如Listing 5-15所示。

Listing 5-15 获取处理器的数量

// 在Galaxy Tab 10.1或者BeBox Dual603将会返回2,在Nexus S或者Logitech Revue将会返回1
final int proc = Runtime.getRuntime().availableProcessors();

通常,available processors的数量是1或者2,尽管将来的产品可能使用4核CPU。当前的安卓笔记本可能已经存在4核架构。依赖于你计划应用什么时候可用,你可能希望仅仅聚焦于1或者2核CPU,稍后发布用于更多核的更新。

NOTE:假设核的数量不总是2的幂次方。

为多核修改算法

第一章给出的一些Fibonacci算法是利用多核的很好的候选。我们从divide-and-conquer算法开始,实现在Listing 5-16给出(和第一章Listing 1-7给出的实现相同)。
Listing 5-16 Fibonacci的Divide-and-Conquer算法

public class Fibonacci {public static BigInteger recursiveFasterBigInteger (int n) {if (n > 1) {int m = (n / 2) + (n & 1);// 两个更简单的子问题BigInteger fM = recursiveFasterBigInteger(m);BigInteger fM_1 = recursiveFasterBigInteger(m - 1);// 结果联合到计算原始问题的方案if ((n & 1) == 1) {// F(m)^2 + F(m-1)^2return fM.pow(2).add(fM_1.pow(2));} else {// (2*F(m-1) + F(m)) * F(m)return fM_1.shiftLeft(1).add(fM).multiply(fM);}return (n == 0)?BigInteger.ZERO : BigInteger.ONE;}}
}

这个算法做了divide-and-conquer算法做的:
(1) 原来的问题是分到更加简单的子问题
(2) 结果联合起来去计算原始问题
因为两个子问题是独立的,可以去并行执行他们,而不需要同步。Java语言定义了ExecutorService接口,几个实现类可以用来schedule要完成的工作。Listing 5-17给出一个示例,使用工厂模式去创建一个线程池。

Listing 5-17 使用ExecutorService

public class Fibonacci {private static final int proc = Runtime.getRuntime().availableProcessors();private static final ExecutorService executorService = Excutors.newFixedThreadPool(proc + 2);public static BigInteger recursiveFasterBigInteger (int n) {// 看Listing 5-16的实现}public static BigInteger recursiveFasterBigIntegerAndThreading (int n) {int proc = Runtime.getRuntime().availableProcessors();if ( n < 128 || proc <= 1) {return recursiveFasterBigInteger(n);}final int m = (n / 2) + (n & 1);Callable<BigInteger> callable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return  recursiveFasterBigInteger(m);}};Future<BigInteger> ffM = executorService.submit(callable);    // 尽早提交第一个jobcallable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return recursiveFasterBigInteger(m - 1);}};Future<BigInteger> ffM_1 = executorService.submit(callable);    // 提交第二个job// 获取部分结果并且联合他们BigInteger fM, fM_1, fN;try {fM = ffM.get();   // 得到第一个子问题的结果 (blocking call)} catch (Exception e) {// 如果有异常,在当前线程计算fMfM = recursiveFasterBigInteger(m);}try {fM_1 = ffM_1.get();  // 得到第二个子问题的结果(blocking call)} catch (Exception e) {// 如果有异常,在当前线程计算fMfM_1 = recursiveFasterBigInteger(m-1);}if ((n & 1) != 0) {fN = fM.pow(2).add(fM_1.pow(2));} else {fN = fM_1.shiftLeft(1).add(fM).multiply(fM);}return fN;}
}

就像你可以清楚的看到的,代码很难阅读。更多的是,这个实现依然基于低性能的代码:就像我们在第一章看到的一样,两个子问题最终将计算许多相同的Fibonacci数。更好的实现是使用cache去记录已经计算的Fibonacci数,会明显的节省时间。Listing 5-18给出了相似的实现,不过使用了cache。
Listing 5-18 使用ExecutorService和Caches

public class Fibonacci {private static final int proc = Runtime.getRuntime().availableProcessors();pirvate static final ExecutorService executorService = Executors.newFixedTrheadPool(proc + 2);private static BigInteger recursiveFasterWithCache (int n, Map<Integer, BigInteger> cache) {// 查看Listing 1-11的实现(有一点不同,因为它使用SparseArray)}public static BigInteger recursiveFasterWithCache (int n) {HashMap<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();return recursiveFasterWithCache(n, cache);}public static BigInteger recursiveFasterWithCacheAndThreading (int n) {int proc = Runtime.getRuntime().availableProcessors();if (n < 128 || proc <= 1) {return recursiveFasterWithCache(n);}final int m = (n / 2) + (n & 1);Callable<BigInteger> callable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return recursiveFasterWithCache(m);}};Future<BigInteger> ffM = executorService.submit(callable);callable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return recursiveFasterWithCache(m - 1);}};Future<BigInteger> ffM_1 = executorService.submit(callable);// 获取部分计算结果并且联合它们BigInteger fM, fM_1, fN;try {fM = ffM.get();   // 获取第一个子问题的结果(blocking call)} catch (Exception e) {// 如果发生异常,在当前线程计算fMfM = recursiveFasterBigInteger(m);}try {fM_1 = ffM_1.get();   // 获取第二个子问题的结果(blocking call)} catch (Exception e) {// 如果发生异常,在当前线程计算fMfM_1 = recursiveFasterBigInteger(m-1);}if ((n & 1) != 0) {fN = fM.pow(2).add(fM_1.pow(2));} else {fN = fM_1.shiftLeft(1).add(fM).multiply(fM);}return fN;}}

使用Concurrent Cache

在这个实现中需要注意的一个事情是每个子问题需要使用自己的cache对象,因此重复的数值仍然将被计算。针对这两个子问题共享一个cache,
我们需要把cache从SparceArray对象改变成允许不同线程同时访问的对象。Listing 5-19给出了这样一个实现,使用ConcurrentHashMap对象作为一个cache。

Listing 5-19 使用ExecutorService和一个Cache

public class Fibonacci {private static final int proc = Runtime.getRuntime().availableProcessors();private static final ExecutorService executorService = Executors.newFixedThreadPool(proc + 2);private static BigInteger recursiveFasterWithCache (int n, Map<Integer, BigInteger> cache) {// 查看Listing 1-11的实现(有一点不同,因为它使用了SparseArray)}public static BigInteger recursiveFasterWithCache (int n) {HashMap<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();return recursiveFasterWithCache(n, cache);}public static BigInteger recursiveFasterWithCacheAndThreading (int n) {int proc = Runtime.getRuntime().availableProcessors();if (n<128 || proc <= 1) {return recursiveFasterWithCache(n);}final ConcurrentHashMap<Integer, BigInteger> cache = new ConcurrentHashMap<Integer, BigInteger>(); // 允许同步访问final int m = (n / 2) + (n & 1);Callable<BigInteger> callable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return recursiveFasterWithCache(m, cache);  // 第一个和第二个job共享同一个cache}};Future<BigInteger> ffM = executorService.submit(callable);callable = new Callable<BigInteger>() {public BigInteger call() throws Exception {return recursiveFasterWithCache(m-1, cache);   // 第一个和第二个job共享cache}};Future<BigInteger> ffM_1 = executorService.submit(callable);// 获取部分计算结果并且联合他们BigInteger fM, fM_1, fN;try {fM = ffM.get();   // 获取第一个子问题的结果(blocking call)} catch (Exception e) {// 如果发生异常, 在当前线程计算fMfM = recursiveFasterBigInteger(m);}try {fM_1 = ffM_1.get();  // 得到第二个子问题的结果(blocking call)} catch (Exception e) {// 如果发生异常,在当前线程计算fMfM_1 = recursiveFasterBigInteger(m-1);}if ((n & 1) != 0) {fN = fM.pow(2).add(fM_1.pow(2));} else {fN = fM_1.shiftLeft(1).add(fM).multiply(fM);}return fN;}
}

NOTE: recursiveFasterWithCache的第二个参数是一个map,所以可以被任何实现Map接口的cache调用,比如ConcurrentHashMap或者HashMap对象。SparseArray对象不是一个map。

通过把一个问题分解为子问题并且为每一个问题分配一个线程,你不会经常观察到性能提升。因为数据之间依然存在依赖性,同步操作需要发生,线程可能花费一部分或者大多数时间去等待数据访问。同样的,性能提升可能不像你期望的那样明显。尽管理论上你可能期望去在双核的处理器上达到双倍在四核的处理器上达到4倍的性能,实际将不是这样的。
实际上,使用多线程去执行无关的任务更加简单(因此避免了同步的需求),如果频率"足够低",需要同步的任务是偶尔的或者经常的。比如,一个video游戏可能通常使用一个线程为了游戏逻辑,另一个线程去做渲染。渲染线程因此需要通过逻辑线程30或者60次每秒手动读取数据(每帧开始渲染的时候),可能相对的很快得到需要渲染一帧数据的copy,因此仅仅block访问很短的时间。

精通安卓性能优化-第五章(三)相关推荐

  1. 精通安卓性能优化-第七章(二)

    关闭Broadcast Receiver 为了保存电量,应用应该避免执行无目的的代码.在上面的实例中,当用户界面不在最前的时候去更新Textview的text是没有价值的,仅仅无必要的从电池提取能量. ...

  2. 《C++应用程序性能优化::第五章动态内存管理》学习和理解

    <C++应用程序性能优化::第五章动态内存管理>学习和理解 说明:<C++应用程序性能优化> 作者:冯宏华等 2007年版. 2010.8.29 cs_wuyg@126.com ...

  3. 安卓性能优化之启动优化

    安卓性能优化之启动优化 两个定律 2-5-8原则 八秒定律 启动方式 冷启动 热启动 温启动 启动耗时统计 系统日志 adb命令 启动耗时分析 CPU Profile 工具介绍 使用方式 数据分析 C ...

  4. Android 性能优化(五)ANR 秒变大神

    Android 性能优化 (一)APK高效瘦身 http://blog.csdn.net/whb20081815/article/details/70140063 Android 性能优化 (二)数据 ...

  5. [译] 2019 前端性能优化年度总结 — 第三部分

    原文地址:Front-End Performance Checklist 2019 - 3 原文作者:Vitaly Friedman 译文出自:掘金翻译计划 本文永久链接:github.com/xit ...

  6. Java 程序性能优化《第一章》Java性能调优概述 1.4小结

    Java 程序性能优化<第一章>1.4小结 通过本章的学习,读者应该了解性能的基本概念及其常用的参考指标.此外,本章还较为详细的介绍了与性能调优相关的两个重要理论--木桶原理以及Amdah ...

  7. 查看linux内存优化,Linux性能优化和监控系列(三) 分析Memory使用状况

    Linux性能优化和监控系列(三) 分析Mem 分析Memory使用状况 内存是影响服务器性能的一个主要因素, 当进程已经驻留内存或者系能够分配给进程足够的内存给它, CPU能顺利自如的运行. 如果发 ...

  8. 安卓性能优化(响应优化)

    姊妹篇:性能优化(内存优化) 安卓app响应速度或使用流畅度是衡量性能的一个指标.如果一个应用用户启动应用时缓慢.使用时卡顿.甚至出现ANR那是很糟糕的体验. 通常,当应用无法响应用户输入时,系统即会 ...

  9. Android性能优化(第二章)

    这章就当填坑了,姑且也算是性能优化吧 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄ 0x00 集合处理 对一些list和数组的操作其实Java已经帮我们做了不少功课了,回想起来在一些使用场景中笔者还傻乎乎的写一堆f ...

最新文章

  1. FILE操作:删除与需要拷贝文件同名的文件
  2. form提交后台注解拿不到数据_Form表单详解
  3. 眼睛看近和看远的示意图
  4. 前端学习(2919):v-bind属性绑定
  5. 微信模版消息 errmsg: 'invalid weapp pagepath hint: [OtU1OA0868a394]
  6. mysql一张表1亿天数据_1亿条数据在PHP中实现Mysql数据库分表100张
  7. POI中设置Excel单元格格式
  8. zabbix自动发现端口
  9. python 错误信息是:sudo :apt-get:command not found
  10. Web分页打印 细线表格+分页打印之终极攻略(转载)
  11. 天线远场定义_高频电磁仿真软件的选型和评估(天线、雷达、电路与器件、无线电总体等)——探讨分享,思路梳理...
  12. 音视频常见码率、帧率等概念介绍
  13. Amazon EKS 版本管理策略与升级流程
  14. 什么是抗攻击服务器?抗攻击服务器是如何防御攻击的?
  15. Mysql NDB Cluster搭建测试
  16. Java后端工程师在做什么
  17. 怎么修复multisim_【血的教训】手把手教你修复崩溃的Windows系统
  18. 无线个人通信(WPAN)-蓝牙
  19. 一点英语不会可以学java吗_不会英语能学编程吗 编程好不好学
  20. 面试官:你能说说Ribbon的负载均衡策略及原理嘛?

热门文章

  1. Linux on IBM Cloud - Port Knocking
  2. 项目管理考PMP真的有用吗?
  3. 整理的一些关于手机拍照技巧的内容
  4. 增量迭代模型,瀑布模型,螺旋模型,快速原型模型
  5. java如何删除购物车里的商品_编写一个简易购物车,实现向购物车内添加商品,移除指定商品及清空购物车功能。...
  6. Qt实现中英文切换(国际化)
  7. 觉醒年代HTML,CSS接下文
  8. Android 关闭屏幕方法
  9. java计算某一天是这一年的第几天
  10. win7(32bit)下完整的搭建apache(2.2.x)+openssl(0.9.6-1.0.1升级)过程