线程安全的HashMap

 一、一般模式下线程安全的HashMap

  默认情况常用的HashMap都是线程不安全的,在多线程的环境下使用,常常会造成不可预知的,莫名其妙的错误。那么,我们如何实现一个线程安全的HashMap呢?其中一个可行的方式是使用Collectons.synchronizedMap() 方法来包装我们的HashMap。如下:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String,String>());

  

Collections.synchronizedMap()会生成一个SynchronizedMap,它使用委托模式,将自己HashMap相关的功能交给传入HashMap实现,二自己负责线程安全的相关实现,下面看看
SynchronizedMap的定义:
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map

        // 使用 mutex 实心对 map 的互斥操作
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

  如在代码中看到的,所有对Map的操作都需要用 这个 mutex  来同步,以实现线程安全。比如说下面这些常见的对HashMap的操作方法:

        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

  除了以上看到的方法之外,其他的Map相关的方法有类似的操作。虽然这个包装的Map可以实现线程安全的要求,但是,它在多线程环境下的性能表现并不是很好,无论是对Map的读取还是写入,偶数需要获得 mutex 的同步锁,这会导致所有对Map的安全操作也会进入等待状态,知道mutex可用。 如果并发级别不高,那么这个 包装的Map可以基本满足要求,但是在搞并发的环境中,我们需要寻找新的解决方案。 ——---> 那就是我们的 ConcurrentHashMap.

二、提高"锁"性能的策略

  1. 减少锁的持有时间

    只在必要时进行同步,减少锁的持有时间。比如说在一个方法中只有一个变量需要同步,那么就没有必要对这整个方法都进行同步,而只需要同步这个变量即可。

        // 无谓的加锁时间
        public synchronied void syncMethod() {

                othrerMethod();
                mutexMethod();
                otherMethod();
        }

        // 正确的加锁时间
        public  void syncMethod() {

                othrerMethod();
                synchronied(this){
                mutexMethod();
                }
                otherMethod();
        }

  2.  减小锁的粒度

    在获取全局信息方法不频繁的时候,通过减小锁的粒度可以搞系统的吞吐量。

  3. 读写分离锁替换独占锁

    在读都写少的情况下,使用读写分离锁,多线程读时不阻塞,而只对写线程进行同步。

  4. 锁分离

    对不同功能的锁进行不同的锁策略。

  5. 锁粗化

    系统对于"锁"的调度也是需要性能消耗的,又是我们可以适当的加大锁的范围,比如说在循环中尽量减少对锁的请求和释放,而是在得到锁的情况,一次性把问题解决。

  

原文地址:https://www.cnblogs.com/ytuan996/p/10585633.html

时间: 2024-11-09 16:17:54

线程安全的HashMap的相关文章

非线程安全的HashMap 和 线程安全的ConcurrentHashMap(转载)

在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作 为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直接从该单例中获取该属性集合.但是,最近发现,HashMap并不是线程安全的,如果你 的单例类没有做代码同步或对象锁的控制,就可能出现异常. 首先看下在多线程的访问下,非现场安全的HashMap的表现如何,在网上看了一些资料,自己也做了一下测试: public class MainClass {  

3.3.2线程安全的HashMap

代码:public class SysHashMao { private static Map<String,String> map= Collections.synchronizedMap(new HashMap<String,String>()); public static void main(String[] args) { map.put("11","11"); map.get("11"); }} 源码解读:可以

浅析HashMap与ConcurrentHashMap的线程安全性

本文要解决的问题: 最近无意中发现有很多对Map尤其是HashMap的线程安全性的话题讨论,在我的理解中,对HashMap的理解中也就知道它是线程不安全的,以及HashMap的底层算法采用了链地址法来解决哈希冲突的知识,但是对其线程安全性的认知有限,故写这篇博客的目的就是让和我一样对这块内容不熟悉的小伙伴有一个对HashMap更深的认知. 哈希表 在数据结构中有一种称为哈希表的数据结构,它实际上是数组的推广.如果有一个数组,要最有效的查找某个元素的位置,如果存储空间足够大,那么可以对每个元素和内

【java并发】造成HashMap非线程安全的原因

0. 写在前面 在前面我的一篇总结线程范围内共享数据文章中提到,为了数据能在线程范围内使用,我用了HashMap来存储不同线程中的数据,key为当前线程,value为当前线程中的数据.我取的时候根据当前线程名从HashMap中取即可. 因为当初学习HashMap和HashTable源码的时候,知道HashTable是线程安全的,因为里面的方法使用了synchronized进行同步,但是HashMap没有,所以HashMap是非线程安全的.在上面提到的例子中,我想反正不用修改HashMap,只需要

问题(一)---线程池,锁、堆栈和Hashmap相关

一.线程池: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间. 如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能. 一个线程池包括以下四个基本组成部分: 1.线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务: 2.工作线程(PoolWorker):线程

HashMap和Hashtable

HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别.主要的区别有:线程安全性,同步(synchronization),以及速度. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行). HashMap是非synchronized,而Hashtable

HashMap的工作原理

这是一节让你深入理解hash_map的介绍,如果你只是想囫囵吞枣,不想理解其原理,你倒是可以略过这一节,但我还是建议你看看,多了解一些没有坏处. hash_map基于hash table(哈希表).哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间:而代价仅仅是消耗比较多的内存.然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的.另外,编码比较容易也是它的特点之一. 其基本原理是:使用一个下标范围比较大的数组来存储元素.可以设计一个函数(哈希函数,也

集合详解之HashMap

HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 1.简单说下HashMap的实现原理: 首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)

线程池的使用(三)

一.概述 今天我们继续研究线程池的使用,因为这块确实可能比较麻烦,不多研究几次是根本不能搞懂的,先看效果图 效果一 效果二 二.代码 效果一(纯ExecutorService.AsyncTask.Runnable关联实现相关文件如下) public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; //任务编号 private static i