小心LinkedHashMap的get()方法(转)
这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。
代码实现如下:
- private ReentrantReadWriteLock lock = new ReentrantReadWriteLock ();
- lruMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, true)
- public V get(K key) {
- lock.readLock().lock();
- try {
- return lruMap.get(key);
- } finally {
- lock.readLock().unlock();
- }
- }
- public int entries() {
- lock.readLock().lock();
- try {
- return lruMap.size();
- } finally {
- lock.readLock().unlock();
- }
- }
- public void put(K key, V value) {
- ...
- lock.writeLock().lock();
- try {
- ...
- lruMap.put(key, value);
- ...
- } finally {
- lock.writeLock().unlock();
- }
- }
在测试中发现问题,跑了几个小时系统就会hung up,无法接收http请求。在将把线程栈打印出来检查后,发现很多http的线程都在等读锁。有一个 runnable的线程hold了写锁,但一直停在LinkedHashMap.transfer方法里。线程栈信息如下:
- "http-0.0.0.0-8081-178" daemon prio=3 tid=0x0000000004673000 nid=0x135 waiting on condition [0xfffffd7f5759c000]
- java.lang.Thread.State: WAITING (parking)
- at sun.misc.Unsafe.park(Native Method)
- - parking to wait for <0xfffffd7f7cc86928> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
- at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:941)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1261)
- at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:594)
- ......
- "http-0.0.0.0-8081-210" daemon prio=3 tid=0x0000000001422800 nid=0x155 runnable [0xfffffd7f5557c000]
- java.lang.Thread.State: RUNNABLE
- at java.util.LinkedHashMap.transfer(LinkedHashMap.java:234)
- at java.util.HashMap.resize(HashMap.java:463)
- at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:414)
- at java.util.HashMap.put(HashMap.java:385)
- ......
大家都知道HashMap不是线程安全的,因此如果HashMap在多线程并发下,需要加互斥锁,如果put()不加锁,就很容易破坏内部链表,造成get()死循 环,一直hung住。这里有一个来自淘宝的例子,有对此现象的详细分析:https://gist.github.com/1081908
但是在MSDP的这个例子中,由于ReentrantReadWriteLock 读写锁的存在,put()和get()方法是互斥,不会有上述读写竞争的问题。
Google后发现这是个普遍存在的问题,其根结在于LinkedHashMap的get()方法会改变数据链表。我们来看一下LinkedHashMap的实现代码:
- public V get(Object key) {
- Entry<K,V> e = (Entry<K,V>)getEntry(key);
- if (e == null)
- return null;
- e.recordAccess(this);
- return e.value;
- }
- void recordAccess(HashMap<K,V> m) {
- LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
- if (lm.accessOrder) {
- lm.modCount++;
- remove();
- addBefore(lm.header);
- }
- }
- void transfer(HashMap.Entry[] newTable) {
- int newCapacity = newTable.length;
- for (Entry<K,V> e = header.after; e != header; e = e.after) {
- int index = indexFor(e.hash, newCapacity);
- e.next = newTable[index];
- newTable[index] = e;
- }
- }
前面LRUCache的代码中,是这样初始化LinkedHashMap的:
lruMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, true)
LinkedHashMap构造函数中的参数true表明LinkedHashMap按照访问的次序来排序。这里所谓的按照访问的次序来排序的含义是:当调用LinkedHashMap 的get(key)或者put(key, value)时,如果key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。正是因为LinkedHashMap提 供按照访问的次序来排序的功能,所以它才需要改写HashMap的get(key)方法(HashMap不需要排序)和HashMap.Entry的recordAccess(HashMap)方法。注 意addBefore(lm.header)是将该entry放在header线性表的最后。(参考LinkedHashMap.Entry extends HashMap.Entry 比起HashMap.Entry多了before, after两个域,是双向的)
在上面的LRUCache中,为了提供性能,通过使用ReentrantReadWriteLock读写锁实现了并发get(),结果导致了并发问题。解决问题的方式很简单, 去掉读写锁,让put()/get()都使用普通互斥锁就可以了。当然,这样get()方法就无法实现并发读了,对性能有所影响。
总结,在使用LinkedHashMap时,请小心LinkedHashMap的get()方法。
http://blog.csdn.net/wawmg/article/details/19482041
小心LinkedHashMap的get()方法(转)的更多相关文章
- JDK source 之 LinkedHashMap原理浅谈
注:本文参考JDK1.7.0_45源码. LinkedHashMap是基于HashMap实现的数据结构,与HashMap主要的不同为每个Entry是使用双向链表实现的,并且提供了根据访问顺序进行排序的 ...
- SQL Server 2014连接不到服务器解决方法
多半是不小心使用qq管家之类软件加速系统时把SQL Server(MSSSQL)不小心关闭了 解决方法如下(以WIN8为例):
- LinkedHashMap源码详解
序言 本来是不打算先讲map的,但是随着对set集合的认识,发现如果不先搞懂各种map,是无法理解set的.因为set集合很多的底层就是用map来存储的.比如HashSet就是用HashMap,Lin ...
- LinkedHashMap实现LRU算法
LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap) ...
- IAR右键无法跳转到定义 的解决方法
用IAR编译程序,有时候编译通过了,但是右键无法GO TO Definition 解决方法有两个: 第一.Tools -> Option -> Project 把Generate br ...
- 如何用LinkedHashMap实现LRU缓存算法
阿里巴巴笔试考到了LRU,一激动忘了怎么回事了..准备不充分啊.. 缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的.LRU这个算法就是把最近一次 ...
- 【源代码】LinkedHashMap源代码剖析
注:下面源代码基于jdk1.7.0_11 之前的两篇文章通过源代码分析了两种常见的Map集合,HashMap和Hashtable.本文将继续介绍还有一种Map集合--LinkedHashMap. 顾名 ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedHashMap
一.前言 在上一篇随笔中,我们分析了HashMap的源码,里面涉及到了3个钩子函数,用来预设给子类--LinkedHashMap的调用,所以趁热打铁,今天我们来一起看一下它的源码吧. 二.Linked ...
- Java中常见数据结构Map之LinkedHashMap
前面已经说完了HashMap, 接着来说下LinkedHashMap. 看到Linked就知道它是有序的Map,即插入顺序和取出顺序是一致的, 究竟是怎样做到的呢? 下面就一窥源码吧. 1, Link ...
随机推荐
- 怎样建立一个bower私库
本教程适用于centos 安装之前 检查nodejs 假设没安装nodejs依照下面步骤安装 $ su - $ yum install openssl-devel $ cd /usr/local/sr ...
- Codeforces Round #258 (Div. 2) 小结
A. Game With Sticks (451A) 水题一道,事实上无论你选取哪一个交叉点,结果都是行数列数都减一,那如今就是谁先减到行.列有一个为0,那么谁就赢了.因为Akshat先选,因此假设行 ...
- boost::property_tree读取解析ini文件--推荐
boost::property_tree读取解析ini文件 #include "stdafx.h" #include <iostream> #include <b ...
- Eclipse中使用版本控制----Git
之前在做软件开发的过程中使用的版本控制软件大多是cvs,svn等等,这些都属于cvcs,及中央版本控制系统,其特点是存在一个中央库,开发者首先从中央库中下载代码,编辑,然后提交.很明显的一个特点就是使 ...
- protected的一些功能
java的访问限制有private.protected.public.这里只想进一步认识一下protected. 我对protected有4个疑问: 1,在相同包中,是否可以调用其它类的protect ...
- git clone cm source & cm vs android version
$ git clone https://github.com/CyanogenMod/android_packages_apps_DeskClock.git -b cm-9.0.0 CM4代表(And ...
- Android SurfaceView实现静态于动态画图效果
本文是基于Android的SurfaceView的动态画图效果,实现静态和动态下的正弦波画图,可作为自己做图的简单参考,废话不多说,先上图, 静态效果: 动态效果: 比较简单,代码注释的也比较详细,易 ...
- Windows Azure入门教学系列 (五):使用Queue Storage
本文是Windows Azure入门教学的第五篇文章. 本文将会介绍如何使用Queue Storage.Queue Storage提供给我们一个云端的队列.我们可以用Queue Storage来进行进 ...
- WPF的MVVM
一.关于WPF WPF(Windows Presentation Foundation) ,从名字来看,Microsoft想把WPF技术作为Windows程序外观(表现层)的基础.我们知道,现在开发 ...
- 【Bootstrap3.0建站笔记二】button可下拉弹出层
1.button可下拉弹出层: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hpbmFwbGFu/font/5a6L5L2T/fontsize/400 ...