一入侯门深似海,大家两年不见了。
1. 开场白
这次复活的是SpringSide-Utils模块,把在唯品会两年的实践抽取出来,做一个大大大的公共类库。一边封装 Guava 和 Apache Common Lang,一边参考移植各门各派的精华:
框架/容器随身自带:Spring,Netty,Tomcat,Jetty,ElasticSearch
专门的类库:Jodd, Apache Common IO,Common Collections,JCTool,OpenHFT,AndroidUtilCode
大厂的开源类库:Facebook JCommon,twitter commons,linkedin-utils
大家可能都忘了,再贴一次门牌:https://git.oschina.net/calvin1978/springside4
希望大家继续发挥点赞党的优良传统,在码云里顺手点个 “Star”
内库中包含了文本、数字、日期、并发、集合、文件、反射、安全等方面的内容等着大家一一探索,这里又再唠叨一下性能,性能,性能。
新库的设计目标,是把最佳实践都封装起来,让大家使用类库时,默认就获得最优的性能。
2. 日期
2.1 DateFormatUtil
日期与String相互转换时,JDK的SimpleDateFormat,又慢,又非线程安全。
在不能全面转为Joda Time时,使用Common Lang的FastDateFormat,又快,又线程安全,还能缓存实例。
2.2 CachingDateFormatter
FastDateFormat再快,日期格式化还是个消耗很大的事情。
参考Logback和Log4j2,在打印当前时间的场景里,将同一时刻的结果缓存。
3. 文本
3.1 StringBuilderHolder
ThreadLocal地重用StringBuilder,节约长字符串拼接时的内存消耗,节约成倍复制扩容的CPU消耗,是OpenHFT等好几个类库的共同选择。
3.2 HashUtil
ThreadLocal地重用SHA1的MessageDiggest,减少每次创建MessageDigest的消耗,也是Tomcat,Facebook等好几个类库的共同选择。
3.3 JsonMapper
封装Jackson的实现,并提供不序列化“值为NULL的属性”等选择。
3.4 TextValidator
判断是否合法的电话,身份证之类的正则表达式校验,Pattern必须得预先编译而不要每次创建,但总有匆忙的同学忘记这点。
3.5 MoreStringUtil
Common Lang的StringUtils已经很好用了。不过字符串操作的消耗总是很大,这里针对一些操作,给出更极致的性能优化,比如split()。
4. 集合
4.1 原子类型集合
当集合中的元素是原子类型,而不是对象的时候。直接以原子类型来存储,不但节约内存(int vs Integer, 4 bytes vs 16 bytes),甚至内部的数据结构也能完全不一样,从而大幅提高性能。
从Netty中移植了IntOjbectHashMap 和 LongObjectHashMap,性能约提升50%。后面还会有IntArrayList等等。
4.2 MapUtil, ListUtil ...
各种集合类的Util的创建函数,强迫大家去思考Array Base 集合类的初始大小,避免了容量不足时的成倍扩容; HashMap的加载因子,减少哈希冲突。
在集合为空或只有一个元素时,使用Java Collections的特殊数据结构,进一步节约内存。
4.3 其他扩展类型
1. Guava
MuitlSet :as MapCounter,不用再自己处理“如果有就+1,没有就放个元素进去”的烦事
MultiMap: as MultiValueMap
WeakConcurrentHashMap: 键值为弱引用的并发Map,只此一家
RangeMap:定义一段范围的Key,对应一个Value,类似一致性哈希环之类的最合适
2. Common Collections:
UniqueArrayList
MultiKeyMap
Flat3Map:如果少于3个元素直接访问属性,否则才访问真正的HashMap
3. Jodd
SortedArrayList
4. JCTools
针对 “多个生产者一个消费者”, “一个生产者多个消费者” 等特定场景优化的Queue。
5. 并发
5.1 JSR166e
JDK的不同版本,不断推出性能更优的并发实现,但如果考虑多JDK版本到的兼容就让人发愁了。好在有Doug Lea大神的JSR166e项目。
1. ThreadLocalRandom
Random本身有全局锁,JDK7的ThreadLocalRandom通过在ThreadLocal里放Random避免了锁。
2. LongAdder
作为计数器,AtomicLong虽然能通过CAS避免锁,但如果线程竞争激烈时依然有很大的损耗。JDK8的LongAdder,根据并发情况,将计数器智能的拆开成若干个,等取值时再求和。
3. ConcurrentHashMapV8
JDK5开始的ConcurrentHashMap是经典的分散锁模式,而JDK8的ConcurrentHashMap,优化后居然取消了锁。
5.2 ThreadPoolBuilder
比JDK Executors,提供更好的线程池设置,比如FixedPool的队列最大长度,CachedPool的最大线程数等。
另提供一个从Tomcat移植的QueueableCachedPool,“支持可变的线程数,跑满线程时任务放队列”这种符合大家想想的场景。
5.3 ThreadLocalContext
提供ThreadLocal HashMap存放上下文的示例,并给出更高效的,使用EnumMap的建议。
6. 反射
6.1 BeanMapper
基于orika封装,同时避免了一些低效API的使用,比如不给出来源集合的类型,让框架自己去反射"iterator"函数的返回值来获取类型的恶劣行为。
更给出了预生成Type类型的最高效的用法。
6.2 FastMethodInvoker
基于cglib,通过代码生成实现最快速的反射调用。比如反射调用A类的“hello” 方法,它就直接生成一个调用a.hello()的FastMethod子类.
7. 其他
7.1 ExceptionUtil
异常构造时,获取当前Stack Trace是一个很耗时的过程,把Stack Trace打印也同样消耗。
如果是一个比较清楚出处的异常,可以通过static定义的静态异常。
但如果异常的message会变化,就不能静态定义唯一的异常了,此时可使用克隆异常,依然避过构造函数。
7.2 SystemPropertiesUtil
Properties本质上是一个有锁的HashTable,所以不能频繁的调用System.getProperty()。提供了一个以回调方式获取变化的ListenableProperties。
一切皆是开始,每天仍在添砖。
希望大家在码云里继续点赞党的天赋气质,顺手点个 “Star”。