【连载】第2章-2.5活跃性与性能(缓存未必带来性能提升) 缓存未必带来性能提升 减小锁粒度 合理的同步代码块大小 耗时长的代码块不要使用锁

格言:在程序猿界混出点名堂!

已经连载了2个周,本书的第2章的解读也要结束了,下周可以开始第3章的解读,说实话,虽然很累,但是每天保持能够坚持读一个章节,慢慢寻味和体会,然后进行组织与总结,把书中最好的东西分享给大家。也是一种快乐。带给快乐的不仅是自己还有你们。另外需要看前面几期的解读,可以添加文章最下面的微信公众号。

《JAVA并发编程实战》解读
【连载】第2章-2.5活跃性与性能

回顾:上一节主要介绍了用锁来构造一种协议来保护共享状态的访问,也介绍了将对象的可变状态封装在对象的内部。这一节还是聊聊同步与活跃性和性能问题的一种微妙关系。

缓存未必带来性能提升

标题里面我们提到了缓存未必带来性能,既然这么说答案是值得深思的,前面章节的将上一次因数分解的计算结果缓存,以便下次可以直接读取缓存,提升性能,但为了保证缓存的lastNumber和lastFactors的原子操作,Servlet的方法上增加synchronized关键字,试想客户端必须串行来执行请求。这其实也违背了Servlet设计的初衷。这种并发称之为不良并发

其实不难看出,是我们锁的粒度太粗,导致性能出现的问题。

减小锁粒度

@ThreadSafe
public class CacheFactorizer implements Servlet{
  @GuardedBy("this") private BigInteger lastNumber;
  @GuardedBy("this") private BigInteger lastFactors;
  @GuardedBy("this") private long hits;
  @GuardedBy("this") private long cacheHits;

  public void service(ServletRequest req,ServletResponse resp){
         BigInteger i = extractFromRequest(req);
         BigInteger factors = null;
         
         synchronized(this){
                hits++;
                if(i.equals(lastNumber)){
                      cacheHits ++;
                      factors = lastFactors.clone();       
                 }
         }
       
         if(factors == null){
              factors = factor(i) ;
              synchronized(this){
                  lastNumber = i;
                   lastFactors = factors;
              }
         }
  }

}

以上代码有几个看点:

  • 不难看出,以上程序减小的锁的粒度,将耗时比较长的factor(i),放在在锁的外边。
  • 为什么hits、cacheHits计数器不使用原子性的AtomicLong来替换呢?使用多种同步器不仅造成混乱,而且对性能提升也带来不了什么优势。
  • 为什么是 factors = lastFactors.clone(); 而不是 factors = lastFactors;?其实这里涉及到一个概念方法逃逸,即lastFactors从方法中逃逸出去,方法以外也能对该变量做修改,导致lastFactors不可控。

合理的同步代码块大小

要判断合理代码块的大小,就需要在安全性、简单性、性能之间平衡。

  • 安全性:即所见即所知,程序表现出的行为和状态期望一致。说白一点就是逻辑没毛病。这一点必须要保证,不能舍弃。
  • 简单性:往往满足简单性,牺牲的往往是性能。比如我们在方法上增加synchronized关键字,逻辑清晰简单。
  • 性能:在安全性保证的前提下,减小锁的粒度,用复杂性换取了性能。
    因此,在设计同步代码时,要在简单性和性能做出平衡选择。

耗时长的代码块不要使用锁

  • 造成线程都在等待,锁的等待时间太长,有可能导致事务未结束数据库连接池的链接占用不释放、微服务下的雪崩等。
  • 还有可能造成死锁,之前提到的银行转账死锁的例子,如果同步代码的时间过长,大大提升了死锁的概率。
    因此,耗时长的代码块,会带来性能和活跃性问题。

喜欢连载可关注简书或者微信公众号
简书专题:Java并发编程实战-可爱猪猪解读
https://www.jianshu.com/c/ac717321a386
微信公众号:可爱猪猪聊程序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章