热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

二,从ConcurrentHashMap类学习高并发程序的设计思路【深入JDK源码】

ConcurrentHashMap是JDK1.5推出的类,性能上比HashTable和Collections.synchronizedMap(newHashMap())快很多。看此类源码最好和

ConcurrentHashMap是JDK 1.5推出的类,性能上比HashTable和Collections.synchronizedMap(new HashMap())快很多。

看此类源码最好和HashTable对比理解,会发现它的优化,此类一出HashTable可废。

 

优化的方向,降低读对锁的依赖,写都是加锁。

一,主要是用了分离锁

1.概括结构如下,可以简单理解成把一个大的HashTable分解成多个,形成了锁分离。

结构说明

ConcurrentHashMap默认是分离了16个模块,即理想状态下能有16个线程同时并发(指要修改的map处于不同的模块之中)。

 

采用分离锁可以避免无意义的等待,相比。在HashTable中实现就简单多了,代码如下

 public synchronized V put(K key, V value) {
...
    }

HashTable是直接在方法前加了synchronized关键字,把整块都锁上了

 

2,应用场景

当有一个大数组时需要在多个线程共享时就可以考虑是否把它给分层多个节点了,避免大锁。并可以考虑通过hash算法进行一些模块定位。

其实不止用于线程,当设计数据表的事务时(事务某种意义上也是同步机制的体现),可以把一个表看成一个需要同步的数组,如果操作的表数据太多时就可以考虑事务分离了(这也是为什么要避免大表的出现),比如把数据进行字段拆分,水平分表等

 

 

二,和使用final变量

 1,  final Segment[] segments;

static final class HashEntry {
        final K key;
        final int hash;
        volatile V value;
        final HashEntry next;
... ...  

//全是final

}

 

这样构建列表主要特性是,并发时你读到的值永远不会改变.  所以读的时候并不需要加锁

  V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry e = getFirst(hash);//这里取的是table的head,因为table用的是volatile也不需要担心并发remove时重排
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value; //当然这里value用的是volatile也是不加锁直接可以的读原因
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck 当然这里是防止当修改时或者remove时的
                    }
                    e = e.next;
                }
            }
            return null;
        }

这里用的Key和 next都是final根本不需要担心链正在被修改,而链的头部,即table[n]是volatile元素,也不需要担心同步

当然增加了final,相应的方法也不对了,最好的就是对比下和HashTable的remove方法

 

2, 当有固定结构,如链表,树等结构时可以尝试把指针即(如next)和元素标识(如key ,id)做final处理,可以学下ConcurrentHashMap的remove方法,不过注意它的头是同步的.

 

三,和volatile变量代替了线程的锁。

在ConcurrentHashMap中,主要定义volatile变量有

transient volatile int count;

transient volatile HashEntry[] table;

 

 

1. 概括.

在Java Memory Model中一般JVM会优化把变量写入当前线程的高速缓存,然后在在先入共享内存中,而Volatile则要求去掉JVM的优化,直接把变量写入JVM的共享内存中,性能上有很大的损耗,从而也失去了程序的原子性。

写个代码测试证明下,如下

View Code
package org.benson.another;
/**
 * 
 * @author BensonHe QQ 277803242 
 * @blogFrom http://www.cnblogs.com/springsource
 * TODO Research for volatile variable
 */
public class Test4Volatile {
    public volatile int volaCount = 0;
    public int nOnVolaCount= 0;
    
    /**
     * run the same job for a volatile and non-volatile variable
     * @param loopCount
     * @return how much the job spend seconds
     */
    public long howLongVolatile(long loopCount) {
        long startTime = java.util.Calendar.getInstance().getTime().getTime();
        for (int i = 0; i )
            volaCount++;
        return java.util.Calendar.getInstance().getTime().getTime() - startTime;
    }
    
    /**
     * run the same job for non-volatile variable
     * @param loopCount
     * @return how much the job spend seconds
     */
    public long howLongNonVolatile(long loopCount) {
        long startTime = java.util.Calendar.getInstance().getTime().getTime();
        for (int i = 0; i )
            nonVolaCount++;
        return java.util.Calendar.getInstance().getTime().getTime() - startTime;
    }


    public static void main(String[] args) {
        Test4Volatile test4Volatile = new Test4Volatile();
        System.out.println("the volatil variable spend "+test4Volatile.howLongVolatile(100000000l));
        System.out.println("the non-volatil variable spend "+test4Volatile.howLongNonVolatile(100000000l));
    }
}

 

运行结果如下

the volatil variable spend 1860
the non-volatil variable spend 284

这就是JVM优化的能力

 

所以要正确的使用variable很重要,看看ConcurrentHashMap中的代码对它的应用,以count为例

   V put(K key, int hash, V value, boolean onlyIfAbsent) {
            lock(); //放进一个同步快中
            try {
                int c = count; //先赋值给一个非volatile变量进行技术
                if (c++ > threshold) // ensure capacity
                    rehash();
                HashEntry[] tab = table;
                int index = hash & (tab.length - 1);
                HashEntry first = tab[index];
                HashEntry e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                V oldValue;
                if (e != null) {
                    oldValue = e.value;
                    if (!onlyIfAbsent)
                        e.value = value;
                }
                else {
                    oldValue = null;
                    ++modCount;
                    tab[index] = new HashEntry(key, hash, first, value);
                    count = c; // 计算完成后在写进共享内存里,write-volatile
                }
                return oldValue;
            } finally {
                unlock();
            }
        }

 

所以,当要对改变量进行计算时,可以把变量赋值到另一个非volatile变量(当然要注意此时计算变量也失去了和共享内存中的一致性,但赋给了原子性了,所以必须放到同步块中增加一致性),进行计算,待计算完成后再赋值给volatile变量,写入共享内存。

2,应用场景推荐

读的操作远远大于写的时候可以用volatile优化,代替同步。

如果只读的话,性能和非volatile都是一样的,并保证一致性

修改代码证明

View Code
package org.benson.another;
/**
 * 
 * @author BensonHe QQ 277803242 
 * @blogFrom http://www.cnblogs.com/springsource
 * TODO Research for volatile variable
 */
public class Test4Volatile {
    public volatile int volaCount = 0;
    public int nOnVolaCount= 0;
    public int readCount;
    
    /**
     * run the same job for a volatile and non-volatile variable
     * @param loopCount
     * @return how much the job spend seconds
     */
    public long howLongVolatile(long loopCount) {
        long startTime = java.util.Calendar.getInstance().getTime().getTime();
        for (int i = 0; i )
            readCount=volaCount;
        return java.util.Calendar.getInstance().getTime().getTime() - startTime;
    }
    
    /**
     * run the same job for non-volatile variable
     * @param loopCount
     * @return how much the job spend seconds
     */
    public long howLongNonVolatile(long loopCount) {
        long startTime = java.util.Calendar.getInstance().getTime().getTime();
        for (int i = 0; i )
            readCount=nonVolaCount;
        return java.util.Calendar.getInstance().getTime().getTime() - startTime;
    }

    public static void main(String[] args) {
        Test4Volatile test4Volatile = new Test4Volatile();
        System.out.println("the volatil variable spend "+test4Volatile.howLongVolatile(100000000l));
        System.out.println("the non-volatil variable spend "+test4Volatile.howLongNonVolatile(100000000l));
    }
}

 

输出

the volatil variable spend 286
the non-volatil variable spend 279

可以看到性能几乎一样

 附:

Volatile 变量法则:对 Volatile 域的写入操作 happens-before 于每个后续对同一 Volatile 的读操作。

如果正确使用Volatile 可以优化线程,使用Volatile原则如下当对一个变量的读远远大于对其修改操作时可以考虑用Volatile优化了。

 

欢迎交流, QQ 107966750

参考资料:

 正确使用 Volatile 变量

 探索 ConcurrentHashMap 高并发性的实现机制

一,初调HashMap,如何修改JDK的源码进行调试 【深入JDK源码】


推荐阅读
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了如何在方法参数中指定一个对象的协议,以及如何调用符合该协议的方法。以一个具体的示例说明了如何在方法参数中指定一个UIView子类对象,并且该对象需要符合PixelUI协议,同时方法需要能够访问该对象的属性。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
author-avatar
辽宁何氏医学院高明月
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有