Map 的线程安全问题

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

如果需要使 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 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3165 引用 • 8206 回帖 • 1 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 171 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 293 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 688 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 7 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 339 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖 • 1 关注
  • 分享

    有什么新发现就分享给大家吧!

    240 引用 • 1729 回帖
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 683 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 106 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1738 回帖 • 3 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    129 引用 • 791 回帖 • 1 关注
  • CodeMirror
    1 引用 • 2 回帖 • 108 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 125 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    15 引用 • 7 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 406 回帖 • 524 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 9 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    1 引用 • 11 回帖 • 1 关注
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 22 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 3 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 16 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    331 引用 • 315 回帖 • 83 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 6 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    84 引用 • 139 回帖