zoukankan      html  css  js  c++  java
  • 深入理解java:2.3.3. 并发编程concurrent包 之容器ConcurrentHashMap

    线程不安全的HashMap

    因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。

    效率低下的HashTable容器

    HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。

    因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。

    如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

    ConcurrentHashMap的锁分段技术

    HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁。

    那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。

    首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

    ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。

    Segment是一种重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,

    HashEntry则用于存储键值对数据。

    一个ConcurrentHashMap里包含一个Segment数组,  每个Segment守护者一个HashEntry数组里的元素,

    当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
    ConcurrentHashMap结构图
     

    ConcurrentHashMap使用方法:

    虽然ConcurrentHashMap是线程安全的,如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。

    但是,在你调用完get后,调用put之前,如果有另外一个线程调用了put(name, x),你再去执行put(name,x),就很可能把前面的操作结果覆盖掉了。

    所以,即使在线程安全的情况下,你还是有可能违反原子操作的规则。

    当然可以用 锁 解决这个问题,但性能受到很大影响。

    另一种思路,也可以使用ConcurrentMap定义的循环CAS方法:

      1. putIfAbsent(K key, V value)  
      2.    如果key对应的value不存在,则put进去,返回null。否则不put,返回已存在的value。  
      3.   
      4. boolean remove(Object key, Object value)  
      5.    如果key对应的值是value,则移除K-V,返回true。否则不移除,返回false。  
      6.   
      7. boolean replace(K key, V oldValue, V newValue)  //循环CAS算法
      8.    如果key对应的当前值是oldValue,则替换为newValue,返回true。否则不替换,返回false。 
    1. public static void demo1() {  
    2.     final Map<String, Integer> count = new ConcurrentHashMap<>();  
    3.     Runnable task = new Runnable() {  
    4.         @Override  
    5.         public void run() {  
    6.             Integer oldValue, newValue;  
    7.             for (int i = 0; i < 5; i++) {  
    8.                 while (true) {  
    9.                     oldValue = count.get("a");  
    10.                     if (null == oldValue) {  
    11.                         newValue = 1;  
    12.                         if (count.putIfAbsent("a", newValue) == null) {  
    13.                             break;  
    14.                         }  
    15.                     } else {  
    16.                         newValue = oldValue + 1;  
    17.                         if (count.replace("a", oldValue, newValue)) {  
    18.                             break;  
    19.                         }  
    20.                     }  
    21.                 }  
    22.             }  
    23.         }  
    24.     };  
    25.     new Thread(task).start();  
    26.     new Thread(task).start();  

    还一种思路,也可以使用Atomic原子操作方法(本质也是循环CAS):

      1. public static void demo1() {  
      2.     final Map<String, AtomicInteger> count = new ConcurrentHashMap<>();  
      3.   
      4.     Runnable task = new Runnable() {  
      5.         @Override  
      6.         public void run() {  
      7.             AtomicInteger oldValue;  
      8.             for (int i = 0; i < 5; i++) {  
      9.                 oldValue = count.get("a");  
      10.                 if (null == oldValue) {  
      11.                     AtomicInteger zeroValue = new AtomicInteger(0);  
      12.                     oldValue = count.putIfAbsent("a", zeroValue);  //赋予初始值:0
      13.                     if (null == oldValue) {  
      14.                         oldValue = zeroValue;  
      15.                     }  
      16.                 }  
      17.                 oldValue.incrementAndGet();  //原子循环CAS自增操作
      18.             }  
      19.            
      20.         }  
      21.     };  
      22.     new Thread(task).start();  
      23.     new Thread(task).start();
  • 相关阅读:
    学生党 应该去 研究研究 Socket(套接字) 实现原理
    收录一些 硬件 相关的 文章
    谈谈 软件 开源项目
    我决定 开启 一个 人工智能 机器学习 技术 的 普及项目 Let it Learn
    我邀请 民科吧 网友 和 三江老师 来 反相吧 辩论 调和级数
    共量子论 丢番图方程组 数值求解 最小分子解
    刚 看了一下 一元三次方程 的 解法
    研究发展 C 语言
    对 量子病态定理 提出的 代数方程 不成立 的 证明
    数学 改革
  • 原文地址:https://www.cnblogs.com/my376908915/p/6759667.html
Copyright © 2011-2022 走看看