001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import java.util.concurrent.atomic.AtomicBoolean; 021import java.util.concurrent.atomic.AtomicLongFieldUpdater; 022import java.util.concurrent.atomic.AtomicReference; 023 024import org.apache.yetus.audience.InterfaceAudience; 025 026/** 027 * High scalable counter. Thread safe. 028 * @deprecated use {@link java.util.concurrent.atomic.LongAdder} instead. 029 */ 030@InterfaceAudience.Public 031@Deprecated 032public class Counter { 033 private static final int MAX_CELLS_LENGTH = 1 << 20; 034 035 private static class Cell { 036 // Pads are added around the value to avoid cache-line contention with 037 // another cell's value. The cache-line size is expected to be equal to or 038 // less than about 128 Bytes (= 64 Bits * 16). 039 040 @SuppressWarnings("unused") 041 volatile long p0, p1, p2, p3, p4, p5, p6; 042 volatile long value; 043 @SuppressWarnings("unused") 044 volatile long q0, q1, q2, q3, q4, q5, q6; 045 046 static final AtomicLongFieldUpdater<Cell> valueUpdater = 047 AtomicLongFieldUpdater.newUpdater(Cell.class, "value"); 048 049 Cell() {} 050 051 Cell(long initValue) { 052 value = initValue; 053 } 054 055 long get() { 056 return value; 057 } 058 059 boolean add(long delta) { 060 long current = value; 061 return valueUpdater.compareAndSet(this, current, current + delta); 062 } 063 } 064 065 private static class Container { 066 /** The length should be a power of 2. */ 067 final Cell[] cells; 068 069 /** True if a new extended container is going to replace this. */ 070 final AtomicBoolean demoted = new AtomicBoolean(); 071 072 Container(Cell cell) { 073 this(new Cell[] { cell }); 074 } 075 076 /** 077 * @param cells the length should be a power of 2 078 */ 079 Container(Cell[] cells) { 080 this.cells = cells; 081 } 082 } 083 084 private final AtomicReference<Container> containerRef; 085 086 public Counter() { 087 this(new Cell()); 088 } 089 090 public Counter(long initValue) { 091 this(new Cell(initValue)); 092 } 093 094 private Counter(Cell initCell) { 095 containerRef = new AtomicReference<>(new Container(initCell)); 096 } 097 098 private static int hash() { 099 // The logic is borrowed from high-scale-lib's ConcurrentAutoTable. 100 101 int h = System.identityHashCode(Thread.currentThread()); 102 // You would think that System.identityHashCode on the current thread 103 // would be a good hash fcn, but actually on SunOS 5.8 it is pretty lousy 104 // in the low bits. 105 106 h ^= (h >>> 20) ^ (h >>> 12); // Bit spreader, borrowed from Doug Lea 107 h ^= (h >>> 7) ^ (h >>> 4); 108 return h; 109 } 110 111 public void add(long delta) { 112 Container container = containerRef.get(); 113 Cell[] cells = container.cells; 114 int mask = cells.length - 1; 115 116 int baseIndex = hash(); 117 if(cells[baseIndex & mask].add(delta)) { 118 return; 119 } 120 121 int index = baseIndex + 1; 122 while(true) { 123 if(cells[index & mask].add(delta)) { 124 break; 125 } 126 index++; 127 } 128 129 if(index - baseIndex >= cells.length && 130 cells.length < MAX_CELLS_LENGTH && 131 container.demoted.compareAndSet(false, true)) { 132 133 if(containerRef.get() == container) { 134 Cell[] newCells = new Cell[cells.length * 2]; 135 System.arraycopy(cells, 0, newCells, 0, cells.length); 136 for(int i = cells.length; i < newCells.length; i++) { 137 newCells[i] = new Cell(); 138 // Fill all of the elements with instances. Creating a cell on demand 139 // and putting it into the array makes a concurrent problem about 140 // visibility or, in other words, happens-before relation, because 141 // each element of the array is not volatile so that you should 142 // establish the relation by some piggybacking. 143 } 144 containerRef.compareAndSet(container, new Container(newCells)); 145 } 146 } 147 } 148 149 public void increment() { 150 add(1); 151 } 152 153 public void decrement() { 154 add(-1); 155 } 156 157 public void set(long value) { 158 containerRef.set(new Container(new Cell(value))); 159 } 160 161 public long get() { 162 long sum = 0; 163 for(Cell cell : containerRef.get().cells) { 164 sum += cell.get(); 165 } 166 return sum; 167 } 168 169 @Override 170 public String toString() { 171 Cell[] cells = containerRef.get().cells; 172 173 long min = Long.MAX_VALUE; 174 long max = Long.MIN_VALUE; 175 long sum = 0; 176 177 for(Cell cell : cells) { 178 long value = cell.get(); 179 sum += value; 180 if(min > value) { min = value; } 181 if(max < value) { max = value; } 182 } 183 184 return new StringBuilder(100) 185 .append("[value=").append(sum) 186 .append(", cells=[length=").append(cells.length) 187 .append(", min=").append(min) 188 .append(", max=").append(max) 189 .append("]]").toString(); 190 } 191}