今天RP爆发,16核服务器load飙到30多,cpu使用情况全部99%以上。
从jstack中分析发现全部线程都堵在map.transfer处,如下:
"pool-10-thread-23" prio=10 tid=0x00007fb190003800 nid=0x6350 runnable [0x00007fb64554b000]
java.lang.Thread.State: RUNNABLE
at java.util.LinkedHashMap.transfer(LinkedHashMap.java:253)
at java.util.HashMap.resize(HashMap.java:564)
at java.util.HashMap.addEntry(HashMap.java:851)
at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:427)
at java.util.HashMap.put(HashMap.java:484)
定位问题:
LinkedHashMap非线程安全(本来是借用linkedHashMap实现LRUCache)
问题分析:
详见:http://coolshell.cn/articles/9606.html
问题解决:
采用google的ConcurrentLinkedHashMap(https://code.google.com/p/concurrentlinkedhashmap/)
Features LRU page replacement policy (currently being upgraded to LIRS). Equivalent performance to ConcurrentHashMap under load. Can bound by the size of the values (e.g. Multimap cache). Can notify a listener when an entry is evicted.
cassandra也在concurrentLinkedHashMap的基础上实现了LRUCache,代码如下(微调):
/**
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
public class LRULinkedHashMap<K, V> {
public static final int DEFAULT_CONCURENCY_LEVEL = 64;
private final ConcurrentLinkedHashMap<K, V> map;
private final AtomicLong requests = new AtomicLong(0);
private final AtomicLong hits = new AtomicLong(0);
private final AtomicLong lastRequests = new AtomicLong(0);
private final AtomicLong lastHits = new AtomicLong(0);
private volatile boolean capacitySetManually;
public LRULinkedHashMap(int capacity) {
this(capacity, DEFAULT_CONCURENCY_LEVEL);
}
public LRULinkedHashMap(int capacity, int concurrency) {
map = new ConcurrentLinkedHashMap.Builder<K, V>().weigher(Weighers.<V> singleton())
.initialCapacity(capacity).maximumWeightedCapacity(capacity)
.concurrencyLevel(concurrency).build();
}
public void put(K key, V value) {
map.put(key, value);
}
public V get(K key) {
V v = map.get(key);
requests.incrementAndGet();
if (v != null)
hits.incrementAndGet();
return v;
}
public V getInternal(K key) {
return map.get(key);
}
public void remove(K key) {
map.remove(key);
}
public long getCapacity() {
return map.capacity();
}
public boolean isCapacitySetManually() {
return capacitySetManually;
}
public void updateCapacity(int capacity) {
map.setCapacity(capacity);
}
public void setCapacity(int capacity) {
updateCapacity(capacity);
capacitySetManually = true;
}
public int getSize() {
return map.size();
}
public long getHits() {
return hits.get();
}
public long getRequests() {
return requests.get();
}
public double getRecentHitRate() {
long r = requests.get();
long h = hits.get();
try {
return ((double) (h - lastHits.get())) / (r - lastRequests.get());
} finally {
lastRequests.set(r);
lastHits.set(h);
}
}
public void clear() {
map.clear();
requests.set(0);
hits.set(0);
}
public Set<K> getKeySet() {
return map.keySet();
}
}
测试:
public static void main(String[] args) {
LRULinkedHashMap<Integer, Integer> cache = new LRULinkedHashMap<Integer, Integer>(5);
Random r = new Random();
for (int i = 0; i < 10; i++) {
int k = r.nextInt(10);
System.out.println("input " + k);
cache.put(k, k);
System.out.println(cache.getKeySet().toString());
}
}
结果如下:
input 1 [1] input 0 [0, 1] input 3 [0, 1, 3] input 4 [0, 1, 4, 3] input 2 [0, 2, 1, 4, 3] input 2 [0, 2, 1, 4, 3] input 4 [0, 2, 1, 4, 3] input 8 [0, 8, 2, 4, 3] input 0 [0, 8, 2, 4, 3] input 2 [0, 8, 2, 4, 3]