一、常用优化组件和方法
1、缓冲
缓冲的一个典型应用是漏斗。
缓冲可以协调上层组件和下层组件的性能差,加快了上层组件的处理速度,从而提升系统的整体性能。
缓冲最常用的场景就是提高IO处理速度。
2、缓存
缓存(Cache)也是一块为提升系统性能而开辟的内层空间。缓存的主要作用是缓存数据处理结果,并提供下次访问使用。
缓存框架:EHCache、OSCache、JBossCache。
基于动态代理的缓存解决方案。
3、对象复用——“池”
对象池化,是目前非常常用的一种系统优化技术。它的核心思想是,如果一个类被频繁请求使用,那么不必每次都生成一个实例,可以将这个类的一些实例保存在一个“池”中,待需要使用的时候直接从池中获取。这个“池”就称为对象池。在实现细节上,它可能是一个数组,一个链表或者任何集合类。
在程序中使用数据库连接池和线程池,可以有效地改善系统在高并发下的性能。
线程池、数据库连接池(C3P0、Proxool)
Apache中,已经提供了一个Jakarata Commons Pool对象池组件。
4、并行代替串行
5、负载均衡
Tomcat集群
黏性Session Session信息平均分配,不具备高可用性
复制Session 所有的Session在所有Tomcat节点保持一致
专门用于分布式缓存框架——Terracotta,可与Jetty、Spring、EHCache集成使用
6、时间换空间
7、空间换时间
二、Java程序优化
1、字符串优化处理
①String对象及其特点
不变性、针对常量池的优化、类的final定义
针对常量池的优化指:当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。当同一个字符串反腐出现时,这个技术可以大幅度节省内存空间。
②subString()方法的内存泄漏
通过偏移量截取字符串,String的原生内容value数组被复制到新的字符串中。(阅读源码可以知道,这是以空间还时间策略)
③字符串分割和查找
StringTokenizer类效率更高、高效率的chatAt()方法
④StringBuffer和StringBuilder
静态字符串的连接操作,Java在编译时做了优化,变量字符串的累加也做了优化,与使用StringBuilder性能一样。
大量循环时,String连接操作性能远低于StringBuilder,对于String操作,类似于"+"和"+="运算符尽量少用。
String的concat()方法效率高于"+"和"+="运算符,但是又远远低于StringBuilder类。
非同步的StringBuilder效率高于同步的StringBuffer
合理设置容量参数
2、核心数据结构
- List接口
ArrayList(线程不安全)与Vector(线程安全)都是数组实现的,随机访问快。
LinkedList是循环双向列表数据结构,随机访问性能很差。
- Map接口
HashMap是线程不安全的,HashTable是线程安全的。LinkedHashMap:有序的,可按插入此书或访问顺序。TreeMap:基于元素的固有排序,内部实现基于红黑树(平衡二叉树)。
- Set接口
元素不能重复
HashSet、LinkedHashSet、TreeSet都是对应的Map的一种封装。
RandomAccess接口是一个标志接口,其实现支持快速随机访问。
3、使用NIO提升性能
为所有的原始类型提供(Buffer)缓存支持
增加通道(channel)对象,作为新的原型I/O抽象
NIO的Buffer类族和Channel
Buffer的三个重要参数:位置(position)、容量(capacity)、上限(limit)
操作:复制缓冲区、缓冲区分片、只读缓冲区、文件映射到内存、处理结构化数据
4、引用类型
①强引用
强引用所指向的对象在任何时候都不会被系统回收,强引用可能导致内存泄漏。
②软引用
③弱引用
④虚引用
虚引用最大的作用在于跟踪对象回收,清理被销毁对象的相关资源。
⑤WeakHashMap类及其实现
WeakHashMap是HashMap的一种实现,它使用弱引用作为内部数据的存储方案。WeakHashMap作为弱引用的一个典型应用:它可以作为简单的缓存表解决方案。
5、有助于改善性能的技巧
(1)慎用异常
例:异常在循环内,就会给系统性能带来很大影响。
(2)使用局部变量
局部变量保存在栈中,速度较快,静态变量、实例变量都在堆中创建,速度较慢。
(3)位运算代替乘除法
(4)替换switch
(5)一维数组代替二维数组
(6)提取表达式
(7)展开循环
(8)布尔运算代替位运算
利用布尔运算的短路
(9)数组复制使用arrayCopy()
因为Systen.arrayCopy()函数是native函数,性能优于普通的函数。
(10)使用Buffer进行I/O操作
合理利用缓冲
(11)使用clone()代替new
注意深拷贝和浅拷贝
(12)用静态方法代替实例方法
工具类的使用
三、并行程序开发及优化
1、并行程序设计模式
(1)Future模式——类似于异步调用,Js中的回调函数。充分利用等待时间。
(2)Master-Worker模式
系统进程由两类进程协作工作:Master进程和Worker进程。Master进程负责接收和分配任务,Worker进程负责处理子任务。当各个Worker进程将子任务处理完成后,将结果返回给Master进程,由Master进程进行归纳和汇总,从而得到系统的最终结果。
Master-Worker模式是一种使用多线程进行数据处理的结构,多个Worker进程协作处理用户请求,Master进程负责维护Worker进程,并整合最终处理结果。
(3)Guarded Suspension模式
意为保护暂停,其核心思想是仅当服务进程准备好时,才提供服务。
有个请求队列充当缓存。
可以和Future模式结合。
(4)不变模式
注意不变模式和只读属性的区别
只读属性不能被其他线程修改,但自身状态可以修改。
(5)生产者——消费者模式
内存缓冲区的主要功能是数据在多线程间的共享,此外通过该缓冲区,可以缓解生产者和消费者之间的性能差。
可以看到许多方面都用到了缓冲技术
2、JDK多任务执行框架
(1)线程池
(2)Executor框架
任务队列:①直接提交的队列 ②有界的任务队列 ③无界的任务队列 ④优先任务队列
拒绝策略
(3)优化线程池大小
N(cpu) = CPU的数量
U(cpu) = 目标CPU的使用率 0 ≤ U(cpu) ≤ 1
W/C = 等待时间与计算时间的比率
最优线程池大小等于:
N(threads) = N(cpu) * U(cpu) * (1 + W/C)
四、JDK并发数据结构
1、并发List
Vector
CopyOnWriteArrayList:当对象进行写操作时,复制该对象,若进行读操作,则直接返回结果,操作过程中不进行同步。
2、并发Set
CopyOnWriteArraySet
3、并发Map
ConcurrentHashMap
性能优于同步的HashMap
4、并发Queue
①以ConcurrentLinkedQueue为代表的高性能队列
②以BlockingQueue接口(常用于多线程间的数据共享)为代表的阻塞队列。
有以下两种实现:
- ArrayBlockingQueue:基于数组
- LinkedBlockingQueue:基于链表
5、并发Deque
双端队列,允许在队列的头部或尾部进行出队、入队操作。
- LinkedList 线程不安全
- ArrayDeque 线程不安全
- LinkedBlockingDeque 线程安全,高并发应用中,性能远远低于LinkedBlockingQueue,更低于ConcurrentLinkedQueue。
五、并发控制方法
- Java内存模型与Volatite
- 同步关键字Synchronized
- ReentrantLock重入锁
- ReadWriteLock读写锁
- COndition对象
- Semaphore信号量
- ThreadLocal线程局部变量
六、“锁”的性能和优化
-
线程的开销
如线程本身的元数据,线程的调度,线程上下文切换等。 -
避免死锁
出现死锁需要满足一下条件:
①互斥条件
②请求与保持条件
③不剥夺条件
④循环等待条件
只要打破死锁必要条件中的任意一个,就能解决死锁问题。 - 减小锁持有时间
- 减小锁粒度
- 读写锁分离来替换独占锁
- 锁分离
- 重入锁和内部锁 (推荐使用内部锁)
- 锁粗化
- 自旋锁
-
锁消除
对锁的请求和释放是要消耗系统资源的,使用锁消除法术可以去掉那些不可能存在多线程访问的锁请求,从而提高系统性能。 - 锁偏向
七、无锁的并行计算
1、非阻塞的同步/无锁
2、原子操作
3、Amino框架
·集合 ·Set ·Amino树 ·Amino图 ·Amino简单调度模式
八、协程
1、协程是对线程的进一步分割,协程间的切换更为轻便。
2、Kilim框架
Task:协程的任务载体
Fibep:用于保存和管理任务的执行堆栈。
Mailbox:协程间的通信载体,用于数据共享和信息交流。