三沣开发知识 购物 网址 游戏 小说 歌词 地图 快照 开发 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 编程 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题
autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程 CSS/HTML/Xhtml
html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
站长资讯 .NET新手 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA VisualStudio ASP.NET-MVC .NET控件开发 EntityFramework WinRT-Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动 Html-Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP OracleERP DynamicsCRM K2 BPM 信息安全 企业信息 Android开发 iOS开发 WindowsPhone WindowsMobile 其他手机 敏捷开发 项目管理 软件工程 SQLServer Oracle MySQL NoSQL 其它数据库 Windows7 WindowsServer Linux
  IT知识库 -> 敏捷开发 -> HashMap与线程安全 -> 正文阅读
 

[敏捷开发]HashMap与线程安全

HashMap与线程安全 HashMap与线程安全
一、HashMap 为何是线程不安全的
    HashMap是通过散列表来实现存储结构的,具体内容请看我的另一篇博客《HashMap深度解析》,那么HashMap为什么线程不安全呢,主要有两个原因。
首先肯定是多个线程同时去往集合里添加数据,第一个原因:两个线程同时添加相同的key值数据,当两个线程同时遍历完桶内的链表时,发现,没有该key值的数据,这是他们同时创建了一个Entry结点,都添加到了桶内的链表上,这样在该HashMap集合中就出现了两个Key相同的数据。第二个原因:当两个线程同时检测到size/capacity>负载因子时,在扩容的时候可能会在链表上产生死循环(为什么会产生死循环,可以看一些HashMap的死循环相关的博客),也可能会产生存储异常。
二、如何线程安全的使用HashMap
方法一:Hashtable
    Hashtable是Java低版本中提出来的,由于其内部的加锁机制,是的其性能较低,目前已经不常用了。所以当一个线程访问Hashtable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,效率很低。
    HashTable源码中是使用synchronized来保证线程安全的,比如下面的get方法和put方法:
public synchronized V get(Object key) {}
public synchronized V put(K key, V value) {}
方法二:SynchronizedMap
    调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized同步关键字来保证对Map的操作是线程安全的。
源码如下

private static class SynchronizedMap<K,V>


    implements Map<K,V>, Serializable {


    // use serialVersionUID from JDK 1.2.2 for interoperability


    private static final long serialVersionUID =1978198479659022715L;


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


        final Object      mutex;    // Object on which to synchronize


    SynchronizedMap(Map<K,V> m) {


            if (m==null)


                throw new NullPointerException();


            this.m = m;


            mutex = this;


        }


    SynchronizedMap(Map<K,V> m, Object mutex) {


            this.m = m;


            this.mutex = mutex;


        }


    public int size() {


        synchronized(mutex) {return m.size();}


        }


    public boolean isEmpty(){


        synchronized(mutex) {return m.isEmpty();}


        }


    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);}


        }


    public V put(K key, V value) {


        synchronized(mutex) {return m.put(key, value);}


        }


    public V remove(Object key) {


        synchronized(mutex) {return m.remove(key);}


        }


    public void putAll(Map<? extends K, ? extends V> map) {


        synchronized(mutex) {m.putAll(map);}


        }


    public void clear() {


        synchronized(mutex) {m.clear();}


    }


    private transient Set<K> keySet = null;


    private transient Set<Map.Entry<K,V>> entrySet = null;


    private transient Collection<V> values = null;


    public Set<K> keySet() {


            synchronized(mutex) {


                if (keySet==null)


                    keySet = new                                     SynchronizedSet<K>(m.keySet(),mutex); 


                return keySet;


            }


    }


    public Set<Map.Entry<K,V>> entrySet() {


            synchronized(mutex) {


                if (entrySet==null)


                    entrySet = new SynchronizedSet<Map.Entry<K,V>>(m.entrySet(), mutex);


                return entrySet;


            }


    }


    public Collection<V> values() {


            synchronized(mutex) {


                if (values==null)


                    values = new SynchronizedCollection<V>(m.values(), mutex);


                return values;


            }


        }


    public boolean equals(Object o) {


            if (this == o)


                return true;


            synchronized(mutex) {return m.equals(o);}


        }


    public int hashCode() {


            synchronized(mutex) {return m.hashCode();}


        }


    public String toString() {


        synchronized(mutex) {return m.toString();}


        }


        private void writeObject(ObjectOutputStream s) throws IOException {


        synchronized(mutex) {s.defaultWriteObject();}


        }


}

方法三:ConcurrentHashMap
    ConcurrentHashMap是java.util.concurrent包中的一个类,
首先,我们先来了解一下这个集合的原理。hashtable是做了同步的,但是性能降低了,因为 hashtable每次同步执行的时候都要锁住整个结构。于是ConcurrentHashMap 修改了其锁住整个结构的格式,改为了只锁住HashMap的一个桶,锁的粒度大大减小,如下图:
 

而且ConcurrentHashMap的读取操作几乎是完全的并发操作。所以ConcurrentHashMap 读操作的加锁加锁粒度变小,个体操作几乎没有锁,所以比起之前的Hashtable大大变快了(这一点在桶更多时表现得更明显些)。只有在求size等操作时才需要锁定整个表。我认为ConcurrentHashMap是线程安全的集合中最高效的。
    而在迭代时,ConcurrentHashMap使用了不同于传统集合的快速失败迭代器的另一种迭代方式,我们称为弱一致迭代器。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出 ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。
在Java 7中使用的是对Segment(Hash表的一个桶)加锁的方式
ConcurrentHashMap中主要实体类就是三个:ConcurrentHashMap(整个Hash表),Segment(桶),HashEntry(节点)。
Segment的源码
static final class Segment<K,V> extends ReentrantLock implements Serializable {
           private static final long serialVersionUID = 2249069246763182397L;
           static final int MAX_SCAN_RETRIES =Runtime.getRuntime().
                availableProcessors() > 1 ? 64 : 1;
           transient volatile HashEntry<K,V>[] table;
           transient int count;
           transient int modCount;
           transient int threshold;
           final float loadFactor;
      }
HashEntry的源码
static final class HashEntry<K,V> { 
    final K key; 
    final int hash; 
    volatile V value; 
    final HashEntry<K,V> next; 

在Java 8中摒弃了Segment的概念,利用CAS算法做了新方式
CAS算法采用“数组+链表+红黑树”的方式实现
                                      ————亓慧杰
  敏捷开发 最新文章
Git~分支真的很轻
分享一个开源免费、目前最好的API接口管理平
论「版本号」的正确使用方式
GitLab~当它是一个源代码管理工具时
Maven依赖解析
如何基于 eolinker 的进行接口管理
Docker与CI持续集成/CD
需求工程的基本过程
信息系统实践手记3
如何打造百亿级数据处理量的弹性调度容器平
上一篇文章      下一篇文章      查看所有文章
加:2017-03-14 02:00:04  更:2017-05-17 02:52:26 
 
技术频道: 站长资讯 .NET新手区 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA Visual Studio ASP.NET MVC .NET控件开发 Entity Framework WinRT/Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动设计 Html/Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP Oracle ERP Dynamics CRM K2 BPM 信息安全 企业信息化其他 Android开发 iOS开发 Windows Phone Windows Mobile 其他手机开发 敏捷开发 项目与团队管理 软件工程其他 SQL Server Oracle MySQL NoSQL 其它数据库 Windows 7 Windows Server Linux
脚本语言: vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程
网站开发: CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年7日历
2018-7-22 22:43:41
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT知识库