内容目录深度剖析ConcurrentHashMap原理及源码深度剖析ConcurrentHashMap段锁设计原理ConcurrentHashMap段锁设计中提高统计元素个数性能的最后一部分,这也是ConcurrentHashMap的巧妙设计。我们知道在调用put方法的时候,ConcurrentHashMap必须增加当前元素的个数,这样才能在size()方法中获取到存储的数据大小。代码的实现如下。finalVputVal(Kkey,Vvalue,booleanonlyIfAbsent){//省略部分代码....addCount(1L,binCount);returnnull;}很多读者可能会问,这段代码有什么值得分析的?请思考一个问题。在常规集合中,我们只需要一个全局的int类型字段来存储元素个数。每添加一个元素,size变量就+1。但是在ConcurrentHashMap集合中,需要保证变量修改的线程安全,那么如何设计呢?是否可以再次使用锁?显然,通过前面的分析,使用同步锁带来的性能开销太大,不适合。因此在ConcurrentHashMap中,采用了自旋锁和段锁的设计。大小计数基本原理分析在ConcurrentHashMap中,使用了两种方法来存储元素个数。当线程竞争不激烈的时候,直接加上baseCount+1,增加元素个数。当线程竞争激烈时,构建一个默认长度为2的CounterCell数组,然后通过随机算法选择一个CounterCell,将值保存在CounterCell中。私有瞬态易失性长基数;privatetransientvolatileCounterCell[]counterCells;累加元素个数的整体流程如下图所示。addCount方法分析addCount方法的完整代码如下。从整体结构来看,它包含两部分逻辑。累加ConcurrentHashMap中的元素个数。使用check>=0判断是否需要扩容。这部分代码在前面分析tranfer()方法时已经提到,这里不再赘述。privatefinalvoidaddCount(longx,intcheck){CounterCell[]as;长b,s;if((as=counterCells)!=null||!U.compareAndSwapLong(this,BASECOUNT,b=baseCount,s=b+x)){CounterCella;长v;诠释米;布尔无竞争=真;if(as==null||(m=as.length-1)<0||(a=as[ThreadLocalRandom.getProbe()&m])==null||!(uncontended=U.compareAndSwapLong(a,CELLVALUE,v=a.value,v+x))){fullAddCount(x,uncontended);返回;}如果(检查<=1)返回;s=sumCount();}if(check>=0){Node[]tab,nt;诠释n,sc;while(s>=(long)(sc=sizeCtl)&&(tab=table)!=null&&(n=tab.length)>>RESIZE_STAMP_SHIFT)!=rs||sc==rs+1||sc==rs+MAX_RESIZERS||(nt=nextTable)==null||transferIndex<=0)中断;如果(U.compareAndSwapInt(this,SIZECTL,sc,sc+1))转移(tab,nt);}elseif(U.compareAndSwapInt(this,SIZECTL,sc,(rs<0){//省略部分代码....}elseif(cellsBusy==0&&counterCells==as&&U.compareAndSwapInt(this,CELLSBUSY,0,1)){booleaninit=false;try{//初始化表if(counterCells==as){CounterCell[]rs=newCounterCell[2];rs[h&1]=newCounterCell(x);counterCells=rs;初始化=真;}}最后{cellBusy=0;}如果(初始化)中断;}}}第二部分,增加元素个数如果CounterCell数组已经初始化,有两种情况。第一种情况是通过(a=as[(n-1)&h])==null当数组下标有null对象时,直接保存当前要加入的元素个数x其中一个对象中的数组。(as=counterCells)!=null&&(n=as.length)>0表示counterCells数组已经初始化。CounterCellr=newCounterCell(x);首先创建一个CounterCell对象并将x保存在其中。u.compareAndSwapInt(this,CELLSBUSY,0,1)当前线程占用锁。rs[j]=r,将新构造的保存元素个数x的CounterCell对象保存到rs[j]的位置。privatefinalvoidfullAddCount(longx,booleanwasUncontended){//节省部分代码....for(;;){CounterCell[]as;反细胞一个;诠释n;长v;if((as=counterCells)!=null&&(n=as.length)>0){if((a=as[(n-1)&h])==null){if(cellsBusy==0){//尝试附加新的CellCounterCellr=newCounterCell(x);//乐观创建if(cellsBusy==0&&U.compareAndSwapInt(this,CELLSBUSY,0,1)){booleancreated=false;try{//在锁下重新检查CounterCell[]rs;诠释米,j;如果((rs=counterCells)!=null&&(m=rs.length)>0&&rs[j=(m-1)&h]==null){rs[j]=r;创建=真;}}最后{cellBusy=0;}如果(创建)中断;继续;//Slot现在非空}}collide=false;}//Omitpartofcode....}//OmitpartCode....}}第二种情况是直接通过U.compareAndSwapLong(a,CELLVALUE,v=a.value,v+x)operationprivatefinalvoidfullAddCount(longx,booleanwasUncontended){//省略部分代码....for(;;){//因为指定下标位置的单元格值不是为空,直接通过cas进行原子累加,成功则直接退出elseif(U.compareAndSwapLong(a,CELLVALUE,v=a.value,v+x))//break;}//省略部分代码....}第三部分,如果CounterCell数组扩容竞争激烈,即通过多次自旋(即当线程不能满足上述判断条件时),扩容CounterCell将被触发。cellsBusy==0&&U.compareAndSwapInt(this,CELLSBUSY,0,1)抢占锁。CounterCell[]rs=newCounterCell[n<<1];在原有基础上增加一倍容量,然后通过for循环进行数据迁移。counterCells=rs;将展开的对象分配给counterCells。privatefinalvoidfullAddCount(longx,booleanwasUncontended){//省略代码....elseif(cellsBusy==0&&U.compareAndSwapInt(this,CELLSBUSY,0,1)){try{if(counterCells==as){//扩展表,除非陈旧//将容量从2加倍到4CounterCell[]rs=newCounterCell[n<<1];for(inti=0;i(long)Integer.MAX_VALUE)?Integer.MAX_VALUE:(int)n);}finallongsumCount(){CounterCell[]as=counterCells;反细胞一个;长总和=baseCount;if(as!=null){for(inti=0;i