这几个月一直在做性能调优的工作,以前总是进行功能的开发,从来不考虑性能的问题,经过这几个月的工作,发现从性能和扩展性的角度去看软件开发,还真是大不一样。在和朋友聊天的时候,提及Java程序是否能充分利用多核cpu的问题的时候,朋友给我推荐了这本书《Programming Concurrency on the JVM》。几天看下来,还真觉得很应景,建议做Java开发的朋友试着阅读一下。我简单记录下我的读后感。

从多线程角度重新检查你的程序

一直习惯于在JavaEE的开源框架下做开发,认为多线程是容器(server)和框架的事情。其实不是的。我们定义的每一个类,如果是在多线程环境下被使用,你就得考虑线程安全和高并发性。

线程安全(thread-safe)VS高并发

所谓线程安全,就是指当同一个对象的状态(属性)被多个线程同时读写的时候,会不会产生冲突和不一致的问题。传统的JDK给我们提供了同步 (synchronize)来避免这个问题,但这往往会造成性能的瓶颈。从JDK5开始,JDK提供了concurrent包,可以帮助我们在很多情况下 避免使用同步来解决线程安全的问题。

设计不变类(immutable), 分离可变类(mutable)

其实,优良的设计是可以避免很多的线程安全问题,并提供高并发和高可用性的支持。最重要的一个设计方法就是设计不变类(immutable)。如果你的类的实例在创建之后就不再能被改变,那么你就不用担心读写冲突,也就是线程安全了,这样你就可以自由的cache和共享这个类的实例了。Hibernate的SessionFactory就是这样设计的,所以SessionFactory是线程安全的。另外JDK的String还有Integer等wrapper类也都是不变类。

当然我们不能避免使用会发生状态变化的类,只是我们要尽量把可变的类和不可变的类分离出来(这其实也是OOD的一个原则)。

使用concurrent包

对于可变的类,也尽量不要使用synchronize。可以使用concurrent提供的lock,这个包提供了读写lock,比synchronize力度更细。其实有点类似于数据库的锁的设计了。

使用Akka - software transactional design (STD)

Akka是一个实现了(STD)的框架,对Java和Scala都有很好的支持。什么是STD呢?其实是借助事务设计的理念来处理并发问题。其实这个也是借鉴了DB的事务设计理念。试想一下,数据库作为一个共享并且可变的资源,能在多线程下工作的那么好,无非借助于优良的锁和事务的机制。STD借鉴了乐观锁的事务机制。STD假设你的共享数据被频繁的读和写,但是同时写的可能性比较小。试想一下,很多时候我们的共享数据都是用户相关的,也就是不同的用户有着不同的可变状态类,只要你保住同一个用户在操作的时候调用的服务不要有并发的问题,也就不会冲突,这大概也是为什么我们一直不怎么注意线程安全同时又没有遇到什么问题的原因。对于一个系统管理的信息,比如几个管理员有可能同时改变某个系统设置,有可能产生并发访问。这种情况很少,一方面因为管理员用户本来就很少,他们同时操作同一个数据的可能性就很少了。而且现在的权限设计很细致,以至于不同的管理员也有不同的管理数据域。但是,我们也不能完全避免并发性,虽然概率比较小。这个时候STD就发挥作用了,它确保在写的时候,如果没有被别的线程捷足先登,它就写进去,万一有别的线程在它读之后,写之前修改了这个数据,它就回滚整个事务,并且retry。这就是乐观锁的思想。

从上面的分析可以看出,在频繁读写同时写冲突很少发生的情况下,可以使用STD取得较好的高并发性。

其实STD,也给我们另一个启事,那就是我们在设计代码的时候,是不是可以加入事务的考虑?那样我们的程序更为安全和合理,Akka提供了这方面的API,很有意思。

使用Akka - Actors

我还没有看到,看完再续

 

待续...