`
tullyliu
  • 浏览: 30950 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ConcurrentHashMap的使用技巧

    博客分类:
  • JUC
阅读更多
在日常开发中,资源池是经常遇到的场景,一种简单的实现是按需创建一个资源,然后放入map中缓存起来,后续使用这个资源时直接从map中获取.

最简单可靠的实现是利用HashedMap+synchronized(或者Lock)
这种方式无疑是正确的,但锁的粒度较大,高并发时性能不佳

改进的一种典型思路是利用JUC里的并发工具ConcurrentHashMap,降低锁粒度,提高并发性
http://dmy999.com/article/34/correct-use-of-concurrenthashmap里提到了一种实践,主要代码如下
private ConcurrentMap records = new ConcurrentHashMap();

private Record getOrCreate(String id) {
    Record rec = records.get(id);
    if (rec == null) {
        // record does not yet exist
        Record newRec = new Record(id);
        rec = records.putIfAbsent(id, newRec);
        if (rec == null) {
            // put succeeded, use new value
            rec = newRec;
        }
    }
    return rec;
}

这种方法直接使用ConcurrentMap,且没有外层的锁,
putIfAbsent方法不会重复放入相同key的对象,
ConcurrentMap内部实现也保证了内存可见性.
看起来是基本正确的.而且大部分情况也是可以正常工作的.

但是这种写法有个致命的问题,那就是
Record newRec = new Record(id);
这个地方在并发情况下可能被多次执行.意味着资源被多次创建.
如果这个资源没有什么外部依赖,多次创建倒是没有问题,不过当遇到
依赖外部资源(文件,socket)时,多次创建就会有报错.

要避免这种资源的多次创建,可以使用double checked的方式实现.
虽然double checked由来已久,且有著名的double checked locking问题
关于此问题(请参见http://en.wikipedia.org/wiki/Double-checked_locking
)的产生主要是java内存模型导致的,但是使用volatile可以避免lock的问题
可能早些时候很多著作里都会提到volatile代价太高,但随着jvm发展(1.5以后),volatile的实现
也变得轻量高效,具体的论述可以参见http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl
注意页面里划掉的一大段论述

Double checked最早是为了解决单例模式产生的,目前java的单例模式已经有更好的
实现方式,比如static变量(dcl的wiki描述),比如enum单例(参见effective java 2nd)

但是在资源池场景仍然可以使用Double checked保证我们资源仅被创建一次
同时尽量降低同步的代价
代码如下
private ConcurrentMap records = new ConcurrentHashMap();

private Record getOrCreate(String id) {
    Record rec = records.get(id);
    if (rec == null) {
        synchronized (this) {
            rec = records.get(id);
            if (rec == null) {
                // record does not yet exist
                Record newRec = new Record(id);
                rec = records.putIfAbsent(id, newRec);
                if (rec == null) {
                    // put succeeded, use new value
                   rec = newRec;
                }
            }
        }
    }
    return rec;
}


注意此处使用了sychronized,仍然不能使用HashedMap代替ConcurrentHashMap
因为这种写法有多线程同时访问Map的get和put方法的可能,
而HashedMap的get和put方法并发环境下也是会出错的.
分享到:
评论

相关推荐

    HashTable、ConcurrentHashMap.pdf

    在Java中,如果需要一个线程安全的哈希表,推荐使用ConcurrentHashMap而不是HashTable。ConcurrentHashMap通过其特有的设计,在保证线程安全的同时,尽可能减少了对性能的负面影响,尤其是在读操作频繁的情况下。...

    java编程技巧典型案例解析

    - 集合框架的并发:了解ConcurrentHashMap、CopyOnWriteArrayList等并发容器的使用。 4. 异常处理 - 异常分类:理解检查异常和运行时异常的区别,以及何时抛出异常。 - try-catch-finally:如何编写优雅的异常...

    JAVA程序设计技巧1001例

    15. **并发容器与并发工具类**:如ConcurrentHashMap、ConcurrentLinkedQueue等,这些容器在多线程环境下提供高效、安全的数据操作。 总之,《JAVA程序设计技巧1001例》是Java开发者的宝贵资源,它通过丰富的实例,...

    java编程小技巧共享

    在适当的情况下,使用`Collections.synchronizedList`或`ConcurrentHashMap`来保证线程安全。 4. ** Lambda表达式**:Java 8引入的Lambda表达式简化了函数式编程,使代码更简洁。学会如何使用函数式接口,如...

    Java性能优化技巧集锦

    2. **集合优化**:选择合适的集合类型,如ArrayList vs LinkedList、HashMap vs ConcurrentHashMap。避免过度创建集合对象,考虑使用集合工厂方法。理解迭代器的使用和并发修改异常。 3. **字符串操作**:字符串是...

    Java编程性能优化技巧有哪些共7页.pdf.zip

    合理使用synchronized,或者利用并发工具类如ConcurrentHashMap,能提高多线程环境下的性能。 3. **有效使用集合操作**:尽量减少集合的迭代次数,避免在循环中进行add、remove等操作,这些操作可能导致不必要的...

    2022年Java性能优化技巧集锦Java教程.docx

    尽量使用并发工具类,如`ConcurrentHashMap`、`ConcurrentLinkedQueue`等,它们提供了线程安全的数据结构,性能通常优于`synchronized`。 12. **适时关闭流** 在使用I/O流后,记得及时关闭,以释放系统资源。不...

    JAVA编程技巧 原代码

    5. **集合框架**:JAVA的集合框架包括List、Set、Map等接口及其实现类,理解它们的特性和应用场景,如ArrayList与LinkedList的区别,HashMap与ConcurrentHashMap的并发处理,以及如何使用泛型来提高代码安全性。...

    Java中的几个HashMapConcurrentHash

    `Java中的几个HashMap ConcurrentHashMap实现分析Java开发Java经验技巧共4页.pdf.zip`这个压缩包文件很可能包含了一些深入的分析和实践案例,可以帮助你更好地理解和运用这些数据结构。在实践中不断探索和总结,是...

    HashMapvsConcurrentHashMap-示例

    总的来说,理解`HashMap`和`ConcurrentHashMap`的工作原理和使用场景对于提升Java开发经验和技巧至关重要。在面对并发问题时,选择正确的数据结构能够大大提高程序的稳定性和效率。通过深入学习和实践,开发者可以更...

    提升Java的锁性能Java开发Java经验技巧共5页.p

    - **ConcurrentHashMap**:在并发环境下,优先考虑使用`ConcurrentHashMap`而非`synchronized`的`HashMap`,因为它内部采用分段锁技术,减少了锁的粒度,提升了并发性能。 - **CopyOnWriteArrayList/...

    java面试技巧,程序员面试技巧

    3. **并发编程**:Java的并发库非常丰富,包括线程池、锁机制(synchronized、ReentrantLock)、并发容器(ConcurrentHashMap、CopyOnWriteArrayList等),需要深入理解其工作原理。 4. **JVM优化**:理解JVM内存...

    部分公司(联想,三星,HTC,多米等)的android面试题暨面试技巧

    7. 调试技巧:学会使用Logcat、Android Profiler、Hprof分析工具等进行调试。 三、Java基础 1. Java语法:包括类、对象、继承、封装、多态等基本概念。 2. 集合框架:List、Set、Map接口的理解,以及ArrayList、...

    回到基础封装集合Java开发Java经验技巧共3页.pdf

    6. **集合的并发处理**:Java提供了Concurrent包,如ConcurrentHashMap和CopyOnWriteArrayList,用于在多线程环境下安全地操作集合。 7. **封装原则**:如何设计类以实现封装,包括私有化成员变量、提供公共的...

    (java .net )程序员面试技巧

    同时,了解并发容器如ConcurrentHashMap和CopyOnWriteArrayList的使用场景。 3. **IO/NIO**:Java IO流的分类、操作模式,以及新引入的NIO非阻塞I/O模型,如Channels、Buffers和Selectors。 4. **多线程**:掌握...

    JAVA面试技巧和IBM、SUN、微软等公司面试题大全

    1. **并发编程**:线程池的使用、并发容器(ConcurrentHashMap、CopyOnWriteArrayList等)。 2. **垃圾回收**:理解GC的工作原理,新生代、老年代、Full GC的触发条件。 3. **JDBC**:数据库连接管理、事务处理、...

    面试大全面试大全

    3. **集合框架**:List(ArrayList, LinkedList)、Set(HashSet, TreeSet)和Map(HashMap, TreeMap, ConcurrentHashMap)的特性和使用场景。面试中可能会让你比较它们的性能和适用情况。 4. **异常处理**:理解...

    java面试(技术、技巧)

    理解并发集合,如ConcurrentHashMap和CopyOnWriteArrayList的特点和使用。 4. **多线程**:理解线程的创建方式(实现Runnable接口和继承Thread类),线程同步(synchronized关键字、wait()、notify()、notifyAll()...

Global site tag (gtag.js) - Google Analytics