小心LinkedHashMap的get()方法

天龙八部TLBB系列 - 网单服务端各目录文件说明【超详细】 专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等) --- 天龙八部服务端各目录详细说明 阅读详情

这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。
   代码实现如下:www.2cto.com
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()方法。

NVIDIA Omniverse 技术浅析(三):NVIDIA RTX 技术 NVIDIA RTX 技术是 NVIDIA 推出的革命性图形渲染技术,旨在通过硬件加速和软件优化,实现高质量的实时图形渲染。RTX 技术在 NVIDIA Omniverse 平台中扮演着至关重要的角色,为其提供了强大的渲染能力。 阅读详情

相关推荐

《小白入门:无人机电调 ESC 的 PWM 接口特性、主流协议与使用指南》

电调是无人机的 “动力中枢”,其 PWM 接口是入门的基础,而 SBUS、DroneCAN 等协议则代表了更高阶的控制方式。小白学习时,需先掌握 PWM 的基本特性和连接方法,再根据场景选择合适的协议,同时严格遵守使用注意事项(如电源匹配、校准步骤、安全操作)。随着实践深入,逐步理解 “协议差异” 和 “系统协同”,才能让无人机更稳定、安全地飞行。

bother3000的博客 3769

linkedHashmap排序机制

LinkedHashMap = HashMap + 双向链表这个说法,对于我们快速理解LinkedHashMap很有启发性。LinkedHashMap不会去改变HashMap节点的性质,LinkedHashMap所做的,只是建立节点之间的联系,LinkedHashMap的增删改查操作,只是在HashMap的增删改查的基础上,进行head和tail指针的移动。总之一句话:LinkedHashMap的所有操作,都是基于HashMap,对自己的双向链表的操作。有点拗口,OK,再换一种说法大家理解一下:Linked

weixin_57763462的博客 5470

【漆学军】MT4进阶到MT5速成之路(2)获取持仓

MT5在持仓获取上,要比MT4复杂些,分两部分,获取持仓和挂单采用了不同的方法。 一、获取持仓中的buy和sell单 int b=0;//获取buy单个数 int s=0;//获取sell单个数 double lot_total_buy=0;//获取持仓中buy单的总手数 double lot_total_sell=0;//获取持仓中sell单的总手数 for(int i=0; i<PositionsTotal(); i++) { ulong ticket=Positi

漆学军的编程笔记 1625

LinkedHashMap是如何实现访问排序的?

源码的核心是数据结构最近在读缓存图片框架的源码,一直不知道缓存是如何判断“LRU” (Least Recently Used)最近最少使用的,看了源码才知道核心是LRUCache类,这个类的核心其实是 LinkedHashMap类关于这个类的介绍,网上有很多资料介绍这个类的特性: 1 双向链表+哈希表 2 线程不安全 3 允许空的键或者值 4 有序 有一篇博客

梁超杰 8298

尝试使用CubeMX做stm32开发之九:SYS配置

参考《STM32中文参考手册_V10》,研究CubeMX中有关SYS的配置

Forster-C的博客 9420

手把手教你用gym训练宇树GO2机器人模型(附完整配置文件)

本文提供了一份详细的实战指南,教你如何使用Gym框架为宇树GO2四足机器人搭建强化学习训练环境。文章深入解析了环境配置、参数调试、奖励函数设置等关键步骤,并分享了避开常见陷阱的经验,旨在帮助开发者高效完成模型训练。

oo7890的博客 322

LinkedHashMap 特性 按插入和访问顺序排序

LinkedHashMap extends HashMapLinkedHashMap的特性:Linked内部含有一个private transient Entry header;来记录元素插入的顺序或者是元素被访问的顺序。利用这个线性结构的对象,可以帮助记录entry加入的前后顺序或者记录entry被访问的频率(最少被访问的entry靠前,最近访问的entry靠后)。大致的过程如下:new Lin

Bryan's Column 3046

【DeepSeek应用实践】手把手教程:用 AnythingLLM + Ollama + DeepSeek-R1 搭建本地企业知识库

在众多的技术方案中,我们选择了 AnythingLLM、Ollama 和 DeepSeek-R1 这一组合来搭建本地知识库。Ollama 作为一个轻量级的本地化大语言模型部署工具,支持多种模型,易于安装和使用,能够快速在本地运行强大的语言模型。DeepSeek-R1 是一个专为知识库问答设计的检索增强生成(RAG)模型,它结合了检索和生成的优势,能够快速从知识库中检索相关信息并生成精准答案。而 AnythingLLM 则是一个用户友好的界面工具,可以方便地连接 Ollama 和 DeepSeek-R1

寻道AI,探索AI无限可能! 2567

LinkedHashMap结构get和put源码流程简析及LRU应用

原理这篇讲得比较透彻Java集合之LinkedHashMap。 本文属于源码阅读笔记,因put,get调用逻辑及链表维护逻辑复杂(至少网上其它文章的逻辑描述及配图,我都没看明白LinkedHashMap到底长啥样),所以以文字描述和手画逻辑图的方式来讲述源码逻辑,阅读时再辅以源码达到事半功倍的效果。 1.LinkedHashMap简要介绍。 LinkedHashMap继承hashMap,并维...

weixin_30904593的博客 1096

【Zephyr存储专题】09_LittleFs文件系统操作

本文档旨在为开发者介绍Zephyr下Littlef's文件系统相关配置使用指南

zero的博客 3246

基于Python的SEC Edgar文件高效爬取:最新技术与实战指南

本文详细介绍了如何使用Python最新技术高效爬取SEC Edgar数据库中的金融文件。我们将探讨SEC Edgar系统的结构、合法爬取注意事项,并使用现代Python库如Requests-HTML、aiohttp和Playwright实现多种爬取方案。文章包含完整的代码示例、性能优化技巧以及数据处理方法,为金融数据分析师、研究人员和开发者提供全面指导。关键词:SEC Edgar、Python爬虫、金融数据、异步爬取、Playwright、XBRL解析。

2201_76125261的博客 633

java集合-(LinkedHashMap)

LinkedHashMap

S1124654的博客 3080

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

小心LinkedHashMap的get()方法(转)

  这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。    代码实现如下:  [java] view plain copy     p...

weixin_34097242的博客 718

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Deb包技术全解:从内部结构到依赖排错实战

软件包管理是Linux生态中应用交付与维护的基础,而.deb格式作为Debian系发行版的核心载体,其背后隐藏着归档结构、依赖关系与安装策略的复杂逻辑。理解deb包本质是一个携带控制信息的归档文件,其依赖字段决定了系统能否正确安装与运行。从dpkg到apt,不同安装工具在依赖处理与自动化层面存在显著差异。掌握控制信息分析、依赖树排查、多架构支持及本地仓库搭建,能有效规避“依赖地狱”等常见故障。在实际运维与开发场景中,无论是离线部署、驱动安装还是输入法组件配置,都离不开对deb包机制的深入理解。本文从基础概念

weixin_34025051的博客 458

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

编码最佳实践(4)--小心LinkedHashMap的get()方法

这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。 代码实现如下: [code="java"]private ReentrantReadWrit...

敖小剑的博客 448

编码最佳实践--小心LinkedHashMap的get()方法

http://www.blogjava.net/aoxj/archive/2012/06/18/381001.html 编码最佳实践(4)--小心LinkedHashMap的get()方法 这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁...

zhangyi0618的专栏 194

map.get,小心get出一个空指针

map.get,get出来的可能是个空,拿着个参数做处理前一定要判断,或者用getOrDefault

未知的希望,是我不灭的信仰 1534

听说同学你搞不懂Java的LinkedHashMap,可笑

同学们好啊,还记得 HashMap 那篇吗?我自己感觉写得非常棒啊,既通俗易懂,又深入源码,真的是分析得透透彻彻、清清楚楚、明明白白的。(一不小心又上仨成语?)HashMap 哪哪都好,...

singwhatiwanna 313

Java集合中的LinkedHashMap类

jdk1.8.0_144   本文阅读最好先了解HashMap底层,可前往《Java集合中的HashMap类》。   LinkedHashMap由于它的插入有序特性,也是一种比较常用的Map集合。它继承了HashMap,很多方法都直接复用了父类HashMap的方法。本文将探讨LinkedHashMap的内部实现,以及它是如何保证插入元素是按插入顺序排序的。   在分析前可以先思考下...

weixin_30780649的博客 71

接口多个实现类加载哪个_实现Map接口(常见)的类的实现原理

5月13号在家写了下,因为之前有准备简历的,结果用的是公司的笔记本,走的时候不小心删除了,就只能重新写了,下面就开始我们的复习之路吧。 首先我们先说下实现map的(常见)类有哪些,如何在稍微的分析下。 Map的实现类(常见):HashMap,TreeMap,HashTable,ConcurrentHashMap(1.8的,一般用于多线程情况下),LinkedHashMap 我...

weixin_39609503的博客 114

Java集合框架深度解析:从数据结构到实战应用

在Java编程中,集合框架是管理对象组的基础工具,其核心原理基于不同的数据结构实现。例如,ArrayList基于动态数组,支持快速随机访问;HashMap则利用哈希表实现高效的键值对存储。理解这些底层数据结构的工作原理,对于优化程序性能至关重要。集合框架的技术价值在于提供了统一、高效的API,能够应对各种数据管理需求,如去重、排序和并发访问。在实际应用场景中,开发者需要根据具体需求选择合适的集合类型,例如在需要快速查找时使用HashSet,在需要保持插入顺序时使用LinkedHashMap。本文通过分析Ar

weixin_34409357的博客 341

Java---- -Map集合的概论及应用、Collections工具类的使用

Map集概论 通过我们查询API文档得到官方说名来了解一下: 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。 Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序...

qq_40883945的博客 224

LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set. get(key) - Get the value (will always be positive) of the key if

Chole1122的专栏 477

Java集合框架实战指南:从数据结构到并发编程的深度解析

数据结构是计算机科学的核心基础,它研究数据的组织、存储和操作方式。在Java开发中,集合框架将经典数据结构如数组、链表、哈希表、红黑树等封装为易用的API,其底层原理直接决定了程序的时间复杂度和空间复杂度。理解这些原理,能帮助开发者编写出更高效、更健壮的代码,避免性能瓶颈和并发问题。集合框架的技术价值在于,它不仅是存储数据的容器,更是实现算法优化、资源管理和并发控制的关键组件。在实际应用场景中,无论是实现高速缓存、任务队列,还是处理海量数据去重、维护有序集合,都需要根据具体需求选择合适的集合类型。例如,Ar

weixin_30896511的博客 313

Java Map遍历性能优化:从keySet到forEach的深度解析与实践指南

在Java集合框架中,Map作为存储键值对的核心数据结构,其遍历操作的性能优化是提升应用效率的关键环节。从底层原理来看,不同的遍历方式如keySet()、entrySet()和values(),本质上是获取Map内部数据的“视图”,而非副本,这直接决定了访问效率。理解HashMap、TreeMap等不同实现的内部结构(如桶数组、链表/红黑树)是选择高效遍历方式的基础。在工程实践中,entrySet()因其一次性获取键值对而成为性能最优的通用选择,尤其适合高并发与大数据量场景。而Java 8引入的Lambda

weixin_30443813的博客 451
上一篇: java synchronized详解
下一篇: Linux Crontab 定时任务 命令详解
Johnny__Tang
博客等级 码龄18年 9粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值