精华内容
下载资源
问答
  • 课程会讲解Java中并发相关技术的基础、原理和应用,从线程安全、线程(池), 锁实现和并发容器等高并发Java实现,去深入理解在并发编程中, 一些最容易被忽视的点,这些点也是我在多年编程经验中实际用到, 对于每...

    立即学习:https://edu.csdn.net/course/play/26270/326883?utm_source=blogtoedu

    一、 ConcurrentHashMap 数据结构

     

    展开全文
  • 我们都知道,HashMap在并发环境下使用可能...在分析HashMap并发问题前,先简单了解HashMap的put和get基本操作是如何实现的。 1.HashMap的put和get操作 大家知道HashMap内部实现是通过拉链法解决哈希冲突的,也就...

    我们都知道,HashMap在并发环境下使用可能出现问题,但是具体表现,以及为什么出现并发问题,
    可能并不是所有人都了解,这篇文章记录一下HashMap在多线程环境下可能出现的问题以及如何避免。

    在分析HashMap的并发问题前,先简单了解HashMap的put和get基本操作是如何实现的。

    1.HashMap的put和get操作

    大家知道HashMap内部实现是通过拉链法解决哈希冲突的,也就是通过链表的结构保存散列到同一数组位置的两个值,

    put操作主要是判空,对key的hashcode执行一次HashMap自己的哈希函数,得到bucketindex位置,还有对重复key的覆盖操作。

    对照源码分析一下具体的put操作是如何完成的:

    涉及到的几个方法:
    数据put完成以后,就是如何get,我们看一下get函数中的操作:
    看一下链表的结点数据结构,保存了四个字段,包括key,value,key对应的hash值以及链表的下一个节点:

    2.Rehash/再散列扩展内部数组长度

    哈希表结构是结合了数组和链表的优点,在最好情况下,查找和插入都维持了一个较小的时间复杂度O(1),
    不过结合HashMap的实现,考虑下面的情况,如果内部Entry[] tablet的容量很小,或者直接极端化为table长度为1的场景,那么全部的数据元素都会产生碰撞,
    这时候的哈希表成为一条单链表,查找和添加的时间复杂度变为O(N),失去了哈希表的意义。
    所以哈希表的操作中,内部数组的大小非常重要,必须保持一个平衡的数字,使得哈希碰撞不会太频繁,同时占用空间不会过大。

    这就需要在哈希表使用的过程中不断的对table容量进行调整,看一下put操作中的addEntry()方法:

    这里面resize的过程,就是再散列调整table大小的过程,默认是当前table容量的两倍。

    关键的一步操作是transfer(newTable),这个操作会把当前Entry[] table数组的全部元素转移到新的table中,
    这个transfer的过程在并发环境下会发生错误,导致数组链表中的链表形成循环链表,在后面的get操作时e = e.next操作无限循环,Infinite Loop出现。

     

    下面具体分析HashMap的并发问题的表现以及如何出现的。

    3.HashMap在多线程put后可能导致get无限循环

    HashMap在并发环境下多线程put后可能导致get死循环,具体表现为CPU使用率100%,
    看一下transfer的过程:


    并发下的Rehash

    1)假设我们有两个线程。我用红色和浅蓝色标注了一下。

    我们再回头看一下我们的 transfer代码中的这个细节:

    1
    2
    3
    4
    5
    6
    7
    do {
        Entry<K,V> next = e.next;// <--假设线程一执行到这里就被调度挂起了
        int i = indexFor(e.hash, newCapacity);
        e.next = newTable;
        newTable = e;
        e = next;
    } while (e != null);

    而我们的线程二执行完成了。于是我们有下面的这个样子。

     

    注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。

    2)线程一被调度回来执行。

    • 先是执行 newTalbe = e;
    • 然后是e = next,导致了e指向了key(7),
    • 而下一次循环的next = e.next导致了next指向了key(3)

     

    3)一切安好。

    线程一接着工作。把key(7)摘下来,放到newTable的第一个,然后把e和next往下移。

     

    4)环形链接出现。

    e.next = newTable 导致  key(3).next 指向了 key(7)

    注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。

     

    于是,当我们的线程一调用到,HashTable.get(11)时,悲剧就出现了——Infinite Loop。

    针对上面的分析模拟这个例子,

    这里在run中执行了一个自增操作,i++非原子操作,使用AtomicInteger避免可能出现的问题:
    测试一下:

    注意并发问题并不是一定会产生,可以多执行几次,

    我试验了上面的代码很容易产生无限循环,控制台不能终止,有线程始终在执行中,

    这是其中一个死循环的控制台截图,可以看到六个线程顺利完成了put工作后销毁,还有四个线程没有输出,卡在了put阶段,感兴趣的可以断点进去看一下:

     

    上面的代码,如果把注释打开,换用ConcurrentHashMap就不会出现类似的问题。

    4.多线程put的时候可能导致元素丢失

    HashMap另外一个并发可能出现的问题是,可能产生元素丢失的现象。

    考虑在多线程下put操作时,执行addEntry(hash, key, value, i),如果有产生哈希碰撞,
    导致两个线程得到同样的bucketIndex去存储,就可能会出现覆盖丢失的情况:

    5.使用线程安全的哈希表容器

    那么如何使用线程安全的哈希表结构呢,这里列出了几条建议:

    使用Hashtable 类,Hashtable 是线程安全的;
    使用并发包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap实现了更高级的线程安全;
    或者使用synchronizedMap() 同步方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。

    展开全文
  • 我们都知道,HashMap在并发环境下使用可能出现问题,但是具体表现,以及为什么出现并发问题,可能并不是所有人都了解,这篇文章记录一下HashMap在多线程环境下可能出现的问题以及如何避免。 在分析HashMap并发...

    我们都知道,HashMap在并发环境下使用可能出现问题,但是具体表现,以及为什么出现并发问题,
    可能并不是所有人都了解,这篇文章记录一下HashMap在多线程环境下可能出现的问题以及如何避免。

    在分析HashMap的并发问题前,先简单了解HashMap的put和get基本操作是如何实现的。

    1.HashMap的put和get操作

    大家知道HashMap内部实现是通过拉链法解决哈希冲突的,也就是通过链表的结构保存散列到同一数组位置的两个值,

    put操作主要是判空,对key的hashcode执行一次HashMap自己的哈希函数,得到bucketindex位置,还有对重复key的覆盖操作

    对照源码分析一下具体的put操作是如何完成的:

    public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            //得到key的hashcode,同时再做一次hash操作
            int hash = hash(key.hashCode());
            //对数组长度取余,决定下标位置
            int i = indexFor(hash, table.length);
            /**
              * 首先找到数组下标处的链表结点,
              * 判断key对一个的hash值是否已经存在,如果存在将其替换为新的value
              */
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
     
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }

    涉及到的几个方法:

    static int hash(int h) {
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
         
    static int indexFor(int h, int length) {
            return h & (length-1);
        }

    数据put完成以后,就是如何get,我们看一下get函数中的操作:

    public V get(Object key) {
            if (key == null)
                return getForNullKey();
            int hash = hash(key.hashCode());
            /**
              * 先定位到数组元素,再遍历该元素处的链表
              * 判断的条件是key的hash值相同,并且链表的存储的key值和传入的key值相同
              */
            for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                    return e.value;
            }
            return null;
    }

    看一下链表的结点数据结构,保存了四个字段,包括key,value,key对应的hash值以及链表的下一个节点:

    static class Entry<K,V> implements Map.Entry<K,V> {
           final K key;//Key-value结构的key
           V value;//存储值
           Entry<K,V> next;//指向下一个链表节点
           final int hash;//哈希值
     }

    2.Rehash/再散列扩展内部数组长度

    哈希表结构是结合了数组和链表的优点,在最好情况下,查找和插入都维持了一个较小的时间复杂度O(1),
    不过结合HashMap的实现,考虑下面的情况,如果内部Entry[] tablet的容量很小,或者直接极端化为table长度为1的场景,那么全部的数据元素都会产生碰撞,
    这时候的哈希表成为一条单链表,查找和添加的时间复杂度变为O(N),失去了哈希表的意义。
    所以哈希表的操作中,内部数组的大小非常重要,必须保持一个平衡的数字,使得哈希碰撞不会太频繁,同时占用空间不会过大。

    这就需要在哈希表使用的过程中不断的对table容量进行调整,看一下put操作中的addEntry()方法:

    void addEntry(int hash, K key, V value, int bucketIndex) {
       Entry<K,V> e = table[bucketIndex];
           table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
           if (size++ >= threshold)
               resize(2 * table.length);
       }

    这里面resize的过程,就是再散列调整table大小的过程,默认是当前table容量的两倍。

    void resize(int newCapacity) {
           Entry[] oldTable = table;
           int oldCapacity = oldTable.length;
           if (oldCapacity == MAXIMUM_CAPACITY) {
               threshold = Integer.MAX_VALUE;
               return;
           }
     
           Entry[] newTable = new Entry[newCapacity];
           //初始化一个大小为oldTable容量两倍的新数组newTable
           transfer(newTable);
           table = newTable;
           threshold = (int)(newCapacity * loadFactor);
       }

    关键的一步操作是transfer(newTable),这个操作会把当前Entry[] table数组的全部元素转移到新的table中,
    这个transfer的过程在并发环境下会发生错误,导致数组链表中的链表形成循环链表,在后面的get操作时e = e.next操作无限循环,Infinite Loop出现。

     

    下面具体分析HashMap的并发问题的表现以及如何出现的。

    3.HashMap在多线程put后可能导致get无限循环 

    HashMap在并发环境下多线程put后可能导致get死循环,具体表现为CPU使用率100%,
    看一下transfer的过程:

    void transfer(Entry[] newTable) {
            Entry[] src = table;
            int newCapacity = newTable.length;
            for (int j = 0; j < src.length; j++) {
                Entry<K,V> e = src[j];
                if (e != null) {
                    src[j] = null;
                    do {
            //假设第一个线程执行到这里因为某种原因挂起
                        Entry<K,V> next = e.next;
                        int i = indexFor(e.hash, newCapacity);
                        e.next = newTable[i];
                        newTable[i] = e;
                        e = next;
                    } while (e != null);
                }
            }
        }

    并发下的Rehash

    1)假设我们有两个线程。我用红色和浅蓝色标注了一下。

    我们再回头看一下我们的 transfer代码中的这个细节:

    do {
        Entry<K,V> next = e.next;// <--假设线程一执行到这里就被调度挂起了
        int i = indexFor(e.hash, newCapacity);
        e.next = newTable[i];
        newTable[i] = e;
        e = next;
    } while (e != null);

    而我们的线程二执行完成了。于是我们有下面的这个样子。

    注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。

    2)线程一被调度回来执行。

    • 先是执行 newTalbe[i] = e;
    • 然后是e = next,导致了e指向了key(7),
    • 而下一次循环的next = e.next导致了next指向了key(3)

    3)一切安好。

    线程一接着工作。把key(7)摘下来,放到newTable[i]的第一个,然后把e和next往下移

    4)环形链接出现。

    e.next = newTable[i] 导致  key(3).next 指向了 key(7)

    注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。

    于是,当我们的线程一调用到,HashTable.get(11)时,悲剧就出现了——Infinite Loop。

    针对上面的分析模拟这个例子,

     

    这里在run中执行了一个自增操作,i++非原子操作,使用AtomicInteger避免可能出现的问题:

    public class MapThread extends Thread{
            /**
             * 类的静态变量是各个实例共享的,因此并发的执行此线程一直在操作这两个变量
             * 选择AtomicInteger避免可能的int++并发问题
             */
             private static AtomicInteger ai = new AtomicInteger(0);
             //初始化一个table长度为1的哈希表
             private static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(1);
             //如果使用ConcurrentHashMap,不会出现类似的问题
    //       private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(1);
                 
             public void run()
              {
                  while (ai.get() < 100000)
                  {  //不断自增
                      map.put(ai.get(), ai.get());
                      ai.incrementAndGet();
                   }
                   
                  System.out.println(Thread.currentThread().getName()+"线程即将结束");
              }
        }

    测试一下:

    public static void main(String[] args){
             MapThread t0 = new MapThread();
             MapThread t1 = new MapThread();
             MapThread t2 = new MapThread();
             MapThread t3 = new MapThread();
             MapThread t4 = new MapThread();
             MapThread t5 = new MapThread();
             MapThread t6 = new MapThread();
             MapThread t7 = new MapThread();
             MapThread t8 = new MapThread();
             MapThread t9 = new MapThread();
              
             t0.start();
             t1.start();
             t2.start();
             t3.start();
             t4.start();
             t5.start();
             t6.start();
             t7.start();
             t8.start();
             t9.start();
              
        }

    注意并发问题并不是一定会产生,可以多执行几次,

    我试验了上面的代码很容易产生无限循环,控制台不能终止,有线程始终在执行中,

    这是其中一个死循环的控制台截图,可以看到六个线程顺利完成了put工作后销毁,还有四个线程没有输出,卡在了put阶段,感兴趣的可以断点进去看一下:

    上面的代码,如果把注释打开,换用ConcurrentHashMap就不会出现类似的问题。

     

    4.多线程put的时候可能导致元素丢失

    HashMap另外一个并发可能出现的问题是,可能产生元素丢失的现象。

    考虑在多线程下put操作时,执行addEntry(hash, key, value, i),如果有产生哈希碰撞,
    导致两个线程得到同样的bucketIndex去存储,就可能会出现覆盖丢失的情况:

    void addEntry(int hash, K key, V value, int bucketIndex) {
        //多个线程操作数组的同一个位置
        Entry<K,V> e = table[bucketIndex];
            table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
            if (size++ >= threshold)
                resize(2 * table.length);
        }

    5.使用线程安全的哈希表容器

    那么如何使用线程安全的哈希表结构呢,这里列出了几条建议:

    使用Hashtable 类,Hashtable 是线程安全的;
    使用并发包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap实现了更高级的线程安全;
    或者使用synchronizedMap() 同步方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。

    转载于:https://www.cnblogs.com/xushuyi/articles/10266280.html

    展开全文
  • HashMap详细介绍一下,怎么计算下标值的,时间复杂度是多少,最坏的时间复杂度是多少,在扩容的时候时间复杂度是O(n)的,你有什么方式去优化这个时间复杂度吗; ConcurrentHashMap的底层实现原理,怎么查找的的,...

    前言

    这些算法,都是小编一点一点看的大佬们的方法,自己积累的.
    如果有什么描述的不对的地方还望大佬赐教
    多交流才能进步,加油,冲冲冲!!!

    网易严选java一面

    基本只问了Java相关的内容

    • 近期做的项目有遇到什么困难吗,怎么解决的;

    • HashMap详细介绍一下,怎么计算下标值的,时间复杂度是多少,最坏的时间复杂度是多少,在扩容的时候时间复杂度是O(n)的,你有什么方式去优化这个时间复杂度吗;

    • ConcurrentHashMap的底层实现原理,怎么查找的的,如何保证查找时的线程安全性;

    • 多线程介绍一下,如果一个方法被synchronized修饰了,你有什么方法可以去掉这个关键字,保证线程安全并且可以提升效率吗;

    • 线程池介绍一下,常用的拒绝策略有哪些;

    • 线程池中一般设置多少线程,你是怎么设定的,为什么;

    • 线程中中常用阻塞队列有哪些,你一般用哪个,LinkedBlockingQueue与ArrayBlockingQueue的优缺点对比;

    • JVM的CMS介绍一下,CMS重新标记时标记什么,为什么这么标记,标记待回收垃圾和标记保留对象的区别是什么,哪个更好一些;

    • Java开发中遇到问题了(比如报了异常),你一般怎么去处理;

    就这些问题,半个小时,因为研究生期间做CV的,项目都是CV相关的,感觉没岗位相关的项目真是弱点,赶紧去网上找个项目水一下了。不确认面试官心里怎么想的,给我一次二面机会吧,有二面再回来写面经。


    网易严选java二面

    面试官挺可爱,上来先自我介绍,然后介绍一个项目。

    • 问项目中redis怎么使用的;

    • 如果自己实现消息队列,选择什么数据结构比较好,怎么实现;

    • MySQL的索引,怎么建立索引,建立索引时有哪些好的习惯;

    • 对于MySQL的了解程度,平时使用MySQL写SQL语句时有哪些心得,SQL的范围查询如何建索引;

    • 继续聊项目,因为项目是CV相关的,所以让我介绍一下深度学习和用到的模型;

    • 平时用过哪些框架;

    面试时间比较短,基本上就是在聊聊项目,然后问一些数据库和Java的基础问题,基本就以上内容。问题比较发散,面试官想到什么有趣的问题就会问出来。所以面试的时候不要说自己没有把握的知识点,不然可能会被面试官逮住问。


    网易严选javaHR面

    在杭州网易大厦现场面试的,十五分钟。面试这么快,一般是两个极端,要么已经打算刷了,要么已经打算要了。

    首先介绍一下在做项目或者实习中,遇到的一些问题,有哪些挑战,怎么解决的,有什么收获;

    在项目中,未来有什么可以提升的地方,怎么去提升;

    未来工作想做什么岗位的工作,有什么打算

    未来职业规划;

    只有一轮hr面,面试结束就让走了。基本上都是在围绕项目来提问的,没有问那些个人情况的问题

    最后

    针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

    image

    上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

    资料领取方式:点击这里免费获取

    image

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

    [外链图片转存中…(img-SijRmqcy-1618484273658)]

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

    展开全文
  • HashMap 是 Java 最长用的集合之一, 在 JDK 源码中,HashMap 的设计非常 Elegant,也包含...HashMap并发插入的过程中会产生什么问题, 又是怎样解决的 当前内容版权归码字科技所有并授权显示,盗版必究。阅读原文
  • Java高并发,如何解决,什么方式解决 对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不...
  • 线程同步指的是多线程的协同,定义多个线程如何访问特定资源,避免多线程并发访问导致数据不一致的问题。ArrayList、LinkkedList、HashMap是最常用的数据结构,但是他们是线程不安全的,在多线程场景下,如果不做...
  • **java基础 ** 02 concurrentHashMap,段锁,如何分段,和hashmap在hash...由于hashMap在处理高并发问题时,会出现因put操作,扩容混乱时出现的链路环,有可能造成下一次操作的死锁。为了解决这个问题提出了:conc...
  • HashMap并发闭环问题?手撕HashMap HashMap简介 HashMap是我们最常用到的集合之一,是java非常典型的数据结构。学习它的源码是非常只有必要的,我们所要了解的并不仅仅是“HashMap不是线程安全的,HashTable是线程...
  • 经常遇到这样的并发问题,相同的订单不允许并发,不同的订单允许并发。(特别是在创建订单,支付的时候,如果相同的订单并发了,很容易出问题) 解决方式如下: 首先定义一个map /**  * 用于防止并发的全局变量...
  • HashMap 如何解决 Hash 冲突? epoll 和 poll 的区别,及其应用场景 简述线程池原理,FixedThreadPool 用的阻塞队列是什么? sychronized 和 ReentrantLock 的区别 sychronized 的自旋锁、偏向锁、轻量级锁、...
  • HashMap可能出现的致命问题:死循环 HashMap在元素插入过多的时候需要进行Resize,Resize的条件是HashMap.Size>=Capacity*LoadFactor HashMap的Resize包括扩容和ReHash两个步骤,ReHash在并发情况下可能会形成...
  • 如何解决hash碰撞 java8中加入了红黑树,怎么变化的 先从这三个方面了解一波 but 我想到之前有个问题 并发修改异常的问题 于是乎我写了一段代码 关于hashMap的 代码如下 Map map = new HashMap() map....
  • 解决并发问题,可以采用, 1、Collections.synchronizedMap(), 2、使用ConcurrentHashMap:采用分段所机制 3、使用HashTable(不推荐,相对线程安全,无法做到完全同步) HashMap如何操作的 1.判断hashmap有...
  • 玩转并发:ConcurrentHashMap实现原理

    千次阅读 2018-05-03 14:21:41
    如何避免HashMap在高并发下的问题呢? 使用ConcurrentHashMap 使用Hashtable 用Collections.synchronizedMap(hashMap)包装成同步集合,原理就是在所有方法上synchronized,点开源码一看就知道 Hashtable实现线程...
  • 1.如何解决HashMap线程不安全的问题? 解决办法:通过同步容器工具类对不安全的集合类进行包装 Map map = (Map) Collections.synchronizedMap(new HashMap<>()); synchronizedMap()源码的put()方法: ...
  • 前言 本人水平有限,此文针对于自认为技术实力对标阿里P7,...HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap 和 HashTable 区别 HashCode 作用,如何重载hashCode方法 ArrayList与LinkList区别.
  • 在Java中绝大部分的集合像什么ArrayList、HashMap等绝大部分不是线程安全的。仅有的线程安全的实现,像...一、在传统的集合框架中,如何解决线程安全问题。 当然,除了Hashtable等同步容器,我们可以使用同步包...
  • 常见面试问题

    2019-05-14 23:04:30
    1.HashMap底层执行原理 2.HashTable和ConcurrentHashMap如何实现线程安全 3.JVM内存布局和垃圾回收机制 4.类加载机制和双亲委派模型 5.简述事务的隔离级别...9.多线程下读概率远大于写概率,如何解决并发问题 ...
  • 面试问题

    2015-03-04 16:38:12
    2 HashMap存储原理 数组与链表结合体,散列冲突怎么解决。 3 程序配置多少内存,如何确定。 4 TreeMap的存储结构是什么??红黑树,自平衡二叉树。 5 一秒处理多少请求,服务器的并发有多少???
  • 集合常见面试题

    2019-04-17 10:12:41
    hashmap如何解决hash冲突,为什么hashmap中的链表需要转成红黑树? hashmap什么时候会触发扩容? jdk1.8之前并发操作hashmap时为什么会有死循环的问题hashmap扩容时每个entry需要再计算一次hash吗? hashmap的...
  • Java面试中常见问题

    2018-07-07 20:31:50
    1、基础部分:...4、有没有遇到过高并发的情况,如何解决?5、写一个冒泡排序算法;6、数据库查询:左连接,内连接、完全连接区别。7、数据库Sql 去重,只保留一条数据。(例:同一个表中由于建表时未设主...
  • Java面试题集(二)

    2017-05-01 14:14:17
    ArrayList和Vector的区别HashMap和Hashtable的区别HashMap和HashSet的区别Java中HashMap工作原理Hashmap多线程下如何解决并发问题Java中hashmap遍历的2种方法keySet和entrySetList 和 Map 区别ListMapSet三个
  • 基础知识

    2020-06-29 09:34:40
    模块化的好处 ...HashMap和ConcurrentHashMap区别及优缺点 对Mysql的了解,和Oracle的区别 对设计模式的看法和认知 有哪些设计模式 如何实现分布式缓存 ...如何解决并发问题?是否进行过相应程序的编写 ...

空空如也

空空如也

1 2 3 4 5
收藏数 85
精华内容 34
关键字:

如何解决hashmap并发问题