Map 的线程安全问题

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

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

    3167 引用 • 8207 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    108 引用 • 54 回帖 • 1 关注
  • PHP

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

    164 引用 • 407 回帖 • 523 关注
  • frp

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

    15 引用 • 7 回帖 • 7 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    311 引用 • 546 回帖 • 38 关注
  • 又拍云

    又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。

    21 引用 • 37 回帖 • 514 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    205 引用 • 357 回帖
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 421 回帖
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 3 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 85 回帖 • 1 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 628 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    491 引用 • 1383 回帖 • 368 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 130 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 39 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 11 关注
  • 游戏

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

    169 引用 • 799 回帖
  • CAP

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

    11 引用 • 5 回帖 • 562 关注
  • SSL

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

    69 引用 • 190 回帖 • 496 关注
  • Swift

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

    34 引用 • 37 回帖 • 496 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    161 引用 • 472 回帖
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    215 引用 • 462 回帖
  • Bug

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

    77 引用 • 1741 回帖
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 593 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 43 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 54 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 9 关注
  • etcd

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

    5 引用 • 26 回帖 • 492 关注
  • 安装

    你若安好,便是晴天。

    128 引用 • 1184 回帖