zoukankan      html  css  js  c++  java
  • JAVA-JDK1.7-ConCurrentHashMap-测试和验证

    概述

    上次记录了关于ConCurrentHashMap的原理以及源码,现在我对其进行有关功能的测试,下面就讲解一下我测试的内容和代码。这些测试主要针对JDK1.7版本。

    GET安全测试

    上一篇写过get方法是没有加锁的,因为HashEntry的value和next属性是volatile的,volatile直接保证了可见性,所以读的时候可以不加锁,现在写一个程序,启动20个线程,只有一个key="count",每个线程都要执行 map.put(key, 1)  或者 map.put(key, value + 1) ,所以理论上说,20个线程会得到值("count",20),所有的源码如下:

     1 package test;
     2 
     3 import java.io.Serializable;
     4 import java.util.concurrent.ConcurrentHashMap;
     5 import java.util.concurrent.ExecutorService;
     6 import java.util.concurrent.Executors;
     7 import java.util.concurrent.locks.ReentrantLock;
     8 
     9 /**
    10  * Created by Administrator on 2019/12/4.
    11  */
    12 public class TestConcurrentHashMap {
    13 
    14 
    15     public static void main(String[] args) {
    16         DoWork dw = new DoWork(map);
    17         //map.put("1",1);
    18         ExecutorService pool = Executors.newFixedThreadPool(8);
    19         try {
    20             for (int i = 0; i < 20; i++) {
    21                 pool.execute(new Thread(dw));// 开启20个线程
    22             }
    23             Thread.sleep(5000);// 主线程睡眠5s 等待子线程完成任务
    24         } catch (Exception e) {
    25             e.printStackTrace();
    26         } finally {
    27             pool.shutdown();// 关闭线程池
    28         }
    29         System.out.println("统计的数量:" + map.get("count"));
    30 
    31     }
    32     private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
    33 
    34     static class DoWork  extends ReentrantLock implements Serializable,Runnable {
    35 
    36         private ConcurrentHashMap<String, Integer> map = null;
    37 
    38         public DoWork(ConcurrentHashMap<String, Integer> map) {
    39             this.map = map;
    40         }
    41 
    42         @Override
    43         public void run() {
    44             add("count");
    45         }
    46 
    47         public void add(String key) {
    48             Integer value = map.get(key);// 获取map中的数值
    49             System.out.println("当前数量" + value);
    50             if (null == value) {
    51                 map.put(key, 1);// 第一次存放
    52             } else {
    53                 map.put(key, value + 1);// 以后次存放
    54             }
    55         }
    56         public void addLock(String key) {
    57             lock();
    58             try {
    59                 Integer value = map.get(key);
    60                 System.out.println("当前数量" + value);
    61                 if (null == value) {
    62                     map.put(key, 1);
    63                 } else {
    64                     map.put(key, value + 1);
    65                 }
    66             } finally {
    67                 unlock();
    68             }
    69         }
    70 
    71 
    72 
    73     }
    74 
    75 }

    在如上测试代码中有如下问题:

    问题1:为什么开启20个线程,启动的线程池确是8个 见第18行 21行代码? 

    回答:这样可以更大的概率实现线程并发。也就是更容易出现问题。

    问题2:为什么要睡眠5s,

    回答:那是因为统计结果的时候尽量确认所有线程执行完了,结果更加准确。

    问题3:ReentrantLock 方法是干嘛的?

    回答:ReentrantLock实现了独占功能,是这里使用的原因。

    当在线程run方法中执行add(String key)方法时候,执行结果如下:

     明显有问题的说,put是有锁的,所以先猜测get存在不安全的嫌疑,现在查看get方法源码,说明get确实没有加锁。所以说明的问题是虽然put有原子性,但是get+put就没有原子性,也就是说,当第一个线程get一个值之后,还没有put进去的时候,就被第二个线程get了,所以第二个线程get的值就是第一个线程put之前的值。

     1 public V get(Object key) {
     2         Segment<K,V> s; // manually integrate access methods to reduce overhead
     3         HashEntry<K,V>[] tab;
     4         int h = hash(key);
     5         long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
     6         if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
     7             (tab = s.table) != null) {
     8             for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
     9                      (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
    10                  e != null; e = e.next) {
    11                 K k;
    12                 if ((k = e.key) == key || (e.hash == h && key.equals(k)))
    13                     return e.value;
    14             }
    15         }
    16         return null;
    17     }

    现在对add方法执行加锁方法,执行测试代码的56行代码的addLock方法。

    然后debug输出是:

     并发效率测试

    现在模拟1000个并发,每个测试1000次操作,循环测试10轮。分别测试Put和Get操作,测试对象HashMapSync、ConcurrentHashMap、Hashtable。源码如下:

      1 package test;
      2 import java.util.Collections;
      3 import java.util.HashMap;
      4 import java.util.Hashtable;
      5 import java.util.Map;
      6 import java.util.concurrent.ConcurrentHashMap;
      7 
      8 import static javafx.scene.input.KeyCode.T;
      9 
     10 
     11 /**
     12  * 测试HashMap和ConcurrentHashMap的并发性能差别。
     13  *
     14  *
     15  */
     16 public class TestConCurrent {
     17     static final int threads = 1000;
     18     static final int NUMBER = 1000;
     19 
     20     public static void main(String[] args) throws Exception {
     21         Map<String, Integer> hashmapSync = Collections
     22                 .synchronizedMap(new HashMap<String, Integer>());
     23         Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<String, Integer>();
     24         Map<String, Integer> hashtable = new Hashtable<String, Integer>();
     25         long totalA = 0;
     26         long totalB = 0;
     27         long totalC = 0;
     28         for (int i = 0; i <= 10; i++) {
     29             totalA += testPut(hashmapSync);
     30             totalB += testPut(concurrentHashMap);
     31             totalC += testPut(hashtable);
     32         }
     33         System.out.println("Put time HashMapSync=" + totalA + "ms.");
     34         System.out.println("Put time ConcurrentHashMap=" + totalB + "ms.");
     35         System.out.println("Put time Hashtable=" + totalC + "ms.");
     36         totalA = 0;
     37         totalB = 0;
     38         totalC = 0;
     39         for (int i = 0; i <= 10; i++) {
     40             totalA += testGet(hashmapSync);
     41             totalB += testGet(concurrentHashMap);
     42             totalC += testGet(hashtable);
     43         }
     44         System.out.println("Get time HashMapSync=" + totalA + "ms.");
     45         System.out.println("Get time ConcurrentHashMap=" + totalB + "ms.");
     46         System.out.println("Get time Hashtable=" + totalC + "ms.");
     47     }
     48     public static long testPut(Map<String, Integer> map) throws Exception {
     49         long start = System.currentTimeMillis();
     50         for (int i = 0; i < threads; i++) {
     51             new MapPutThread(map).start();
     52         }
     53         while (MapPutThread.counter > 0) {
     54             Thread.sleep(1);
     55         }
     56         return System.currentTimeMillis() - start;
     57     }
     58     public static long testGet(Map<String, Integer> map) throws Exception {
     59         long start = System.currentTimeMillis();
     60         for (int i = 0; i < threads; i++) {
     61             new MapGetThread(map).start();
     62         }
     63         while (MapGetThread.counter > 0) {
     64             Thread.sleep(1);
     65         }
     66         return System.currentTimeMillis() - start;
     67     }
     68 }
     69 class MapPutThread extends Thread {
     70     static int counter = 0;
     71     static Object lock = new Object();
     72     private Map<String, Integer> map;
     73     private String key = this.getId() + "";
     74     MapPutThread(Map<String, Integer> map) {
     75         synchronized (lock) {
     76             counter++;
     77         }
     78         this.map = map;
     79     }
     80     public void run() {
     81         for (int i = 1; i <= TestConCurrent.NUMBER; i++) {
     82             map.put(key, i);
     83         }
     84         synchronized (lock) {
     85             counter--;
     86         }
     87     }
     88 }
     89 class MapGetThread extends Thread {
     90     static int counter = 0;
     91     static Object lock = new Object();
     92     private Map<String, Integer> map;
     93     private String key = this.getId() + "";
     94     MapGetThread(Map<String, Integer> map) {
     95         synchronized (lock) {
     96             counter++;
     97         }
     98         this.map = map;
     99     }
    100     public void run() {
    101         for (int i = 1; i <= TestConCurrent.NUMBER; i++) {
    102             map.get(key);
    103         }
    104         synchronized (lock) {
    105             counter--;
    106         }
    107     }
    108 }

    如上代码只是测试效率的简单测试: 

    测试结果是:

    总结

    能动手的尽量不要猜,能用代码的尽量不要相信原理。

    感谢网络大神,参考链接:

    https://blog.csdn.net/java2000_net/article/details/3373181

    https://www.cnblogs.com/yanphet/p/5726919.html

     

  • 相关阅读:
    day06 字典、元组、set的方法及常用操作
    python makestrans translate
    python 中locals() 和 globals()
    threading.local()
    进程 线程 协程
    微信机器人
    flask
    python is ==
    Beautiful Soup 4.4.0 基本使用方法
    12306
  • 原文地址:https://www.cnblogs.com/boanxin/p/11966629.html
Copyright © 2011-2022 走看看