Map 的线程安全问题

本贴最后更新于 3036 天前,其中的信息可能已经事过境迁

如果需要使 Map 线程安全,大致有这么四种方法: 
1、使用 synchronized 关键字,代码如下 

Java代码  
  1. synchronized(anObject) {     
  2.     value = map.get(key);  
  3. }  

  

2、使用 JDK1.5提供的锁(java.util.concurrent.locks.Lock)。代码如下 

Java代码  
  1. lock.lock();     
  2. value = map.get(key);     
  3. lock.unlock();  

  

3、使用 JDK1.5 提供的读写锁(java.util.concurrent.locks.ReadWriteLock)。代码如下 

Java代码  
  1. rwlock.readLock().lock();     
  2. value = map.get(key);     
  3. rwlock.readLock().unlock();  

  
这样两个读操作可以同时进行,理论上效率会比方法 2 高。 

4、使用 JDK1.5 提供的 java.util.concurrent.ConcurrentHashMap 类。该类将 Map 的存储空间分为若干块,每块拥有自己的锁,大大减少了多个线程争夺同一个锁的情况。代码如下 
value = map.get(key); //同步机制内置在 get 方法中 

比较: 

1、不同步确实最快,与预期一致。 
2、四种同步方式中,ConcurrentHashMap 是最快的,接近不同步的情况。 
3、synchronized 关键字非常慢,比使用锁慢了两个数量级。如果需自己实现同步,则使用 JDK1.5 提供的锁机制,避免使用 synchronized 关键字。 

Java代码  
  1. public class MapTest{     
  2.     public static final int THREAD_COUNT = 1;     
  3.     public static final int MAP_SIZE = 1000;     
  4.     public static final int EXECUTION_MILLES = 1000;     
  5.     public static final int[] KEYS = new int[100];            
  6.     public static void main(String[] args) throws Exception{     
  7.   
  8.         //初始化     
  9.         Random rand = new Random();     
  10.         for (int i = 0; i < KEYS.length; ++i)  KEYS[i] = rand.nextInt();     
  11.      //创建线程     
  12.       long start = System.currentTimeMillis();     
  13.       Thread[] threads = new Thread[THREAD_COUNT];     
  14.       for (int i = 0; i < THREAD_COUNT; ++i) {     
  15.           threads[i] = new SynchronizedThread();     
  16.           //threads[i] = new LockThread();     
  17.            threads[i].start();     
  18.       }     
  19.   
  20.        //等待其它线程执行若干时间     
  21.         Thread.sleep(EXECUTION_MILLES);  
  22.         //统计 get 操作的次数     
  23.         long sum = 0;            
  24.         for (int i = 0; i < THREAD_COUNT; ++i){     
  25.         sum += threads[i].getClass().getDeclaredField("count").getLong(threads[i]);           }     
  26.         long millisCost = System.currentTimeMillis() - start;     
  27.         System.out.println(sum + "(" + (millisCost) + "ms)");     
  28.         System.exit(0);     
  29.     }     
  30.          
  31.     public static void fillMap(Map<Integer, Integer> map){     
  32.         Random rand = new Random();  
  33.         for (int i = 0; i < MAP_SIZE; ++i){     
  34.             map.put(rand.nextInt(), rand.nextInt());     
  35.         }     
  36.     }     
  37. }     
  38. class SynchronizedThread extends Thread{     
  39.     private static Map<Integer, Integer> map = new HashMap<Integer, Integer>();      
  40.     public long count = 0;  
  41.     static {     
  42.         MapTest.fillMap(map);     
  43.     }     
  44.     public void run()  {     
  45.         for (;;) {     
  46.             int index = (int)(count % MapTest.KEYS.length);     
  47.             synchronized(SynchronizedThread.class){     
  48.                 map.get(MapTest.KEYS[index]);     
  49.             }     
  50.             ++count;     
  51.         }     
  52.     }     
  53. }     
  54.   
  55.   
  56.   
  57. class LockThread extends Thread{     
  58.     private static Map<Integer, Integer> map = new HashMap<Integer, Integer>();      
  59.     private static Lock lock = new ReentrantLock();     
  60.     public long count = 0;           
  61.     static {     
  62.         MapTest.fillMap(map);     
  63.     }            
  64.     public void run() {     
  65.         for (;;) {     
  66.             int index = (int)(count % MapTest.KEYS.length);     
  67.             lock.lock();     
  68.             map.get(MapTest.KEYS[index]);     
  69.             lock.unlock();     
  70.             ++count;     
  71.         }     
  72.     }     
  73. }  




以下两种写法的区别: 

Java代码  
  1. synchronized(anObject)    
  2. {    
  3.     value = map.get(key);    
  4. }  
  5. synchronized(anObject)    
  6. {    
  7.     map.put(key, value);    
  8. }  


这样因该是线程安全的,只要保证put和get都同步到这个anObject上来 

Java代码  
  1. synchronized(key)    
  2. {    
  3.     value = map.get(key);    
  4. }   
  5. synchronized(key)    
  6. {    
  7.     map.put(key, value);   
  8. }  


这种写法可能会有问题,因为get和put的key可能是不同的对象

  • 线程
    120 引用 • 111 回帖 • 3 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8208 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 624 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    69 引用 • 190 回帖 • 474 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 446 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 23 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    41 引用 • 40 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 60 回帖 • 464 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    5 引用 • 26 回帖 • 499 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 580 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 65 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖 • 5 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    285 引用 • 4482 回帖 • 658 关注
  • OnlyOffice
    4 引用 • 12 关注
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 2 关注
  • CodeMirror
    1 引用 • 2 回帖 • 125 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    26 引用 • 85 回帖
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 348 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 52 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    171 引用 • 814 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 607 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 510 关注
  • 创业

    你比 99% 的人都优秀么?

    83 引用 • 1398 回帖