Map线程安全的面试题

个人博客

欢迎访问个人博客: https://www.crystalblog.xyz/

备用地址: https://wang-qz.gitee.io/crystal-blog/

1. Hashtable与HashMap的区别

(1), HashMap实现不同步, 线程不安全. HashMap中的key-value都是存储在Entry中的.

​ Hashtable线程安全. 使用synchronized .

​ jdk7 底层数据结构: 数组 + 链表.

​ jdk8 底层数据结构: 数组 + 链表 + 红黑树.

(2), 继承不同. HashMap继承AbstractMap , Hashtable继承Dictionary类.

(3), Hashtable中的方法是同步的, 而HashMap中的方法在缺省情况下是非同步的, 在多线程并发环境下, 可以直接使用Hashtable. 但是要使用HashMap就要自己增加同步处理.

(4), HashMap中key和value允许为null.

​ Hashtable中, key和value都不允许为null值.

(5), 哈希值的使用不同, HashTable直接使用对象的hashCode值, HashMap是重新计算hash值.

2. Hashtable有哪些缺陷? 为什么很少使用Hashtable?

Hashtable底层使用 synchronized 保证线程安全性问题. 线程安全性问题: 加锁

synchronized 锁的升级过程: 偏向锁, 轻量锁, 重量锁.

(1), 传统Hashtable是采用synchronized 将整个Hashtable中的数组锁住, 但在多个线程情况下, 只允许一个线程访问put或get操作, 效率非常低, 但是能够保证线程安全.

(2), Jdk官方不推荐在多线程的情况下使用Hashtable或者HashMap, 建议使用ConcurrentHashMap分段HashMap, 效率非常高.

核心思想: 减少多个线程下的锁竞争, 不会访问同一个Hashtable.

(3), Jdk8中, ConcurrentHashMap取消了分段锁设计, ConcurrentHashMap的get方法没有锁的竞争; Hashtable的get方法有锁的竞争.

请添加图片描述
上图中, A线程获取到this锁进行get操作, B线程的put就无法操作.

public class HashtableTest {

    public static void main(String[] args) {
        // a,b线程使用同一个Hashtable, 会发生this锁的竞争
        Hashtable<Object, Object> hashtable = new Hashtable<>();
        // a线程, get操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "---" + hashtable.get("key"));
            }
        }, "a线程").start();

        // b线程,put操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "---" + hashtable.put("key", "value"));
            }
        }, "b线程").start();
    }
}

3. ConcurrentHashMap 1.7 底层实现

ConcurrentHashMap 将一个大的HashMap集合拆分成n多个不同的小Hashtable(Segment), 默认的情况下是分成16个不同的Segment. 每个Segment中都有自己独立的HashEntry<K,V>[] table;
请添加图片描述上图中, 如果多个线程写入的key最终计算落地到不同的小的Hashtable集合中, 就可以实现多线程同时写入key, 不发生锁的竞争. 但是如果多个线程写入的key最终落地到同一个小的Hashtable集合中, 就会发生锁的竞争.
请添加图片描述

4. ConcurrentHashMap分段锁设计

ConcurrentHashMap 底层采用分段锁设计, 将一个大的Hashtable线程安全的集合拆分成多个小的Hashtable集合,默认初始化16个小的Hashtable集合.

如果同时16个线程最终计算的index值落地到不同的小的Hashtable集合, 不会发生锁的竞争, 同时可以支持16个线程访问ConcurrentHashMap写的操作, 效率非常高.

Jdk7中, ConcurrentHashMap需要计算出2次index值:

第一次计算index值, 计算key存放到具体小的Hashtable

第二次计算index值, 计算key存放到具体小的Hashtable中对应具体的index值

5. 如何手写出一个ConcurrentHashMap

public class MyConcurrentHashMap<K, V> {

    private Hashtable<K, V>[] hashtables;

    public MyConcurrentHashMap() {
       // 初始化16个长度的Hashtable数组
        hashtables = new Hashtable[16];
        for (int i = 0; i < hashtables.length; i++) {
            hashtables[i] = new Hashtable<K, V>();
        }
    }

   // put方法
    public void put(K k, V v) {
       // 根据hashcode计算存放的index
        int hashtableIndex = k.hashCode() % hashtables.length;
        hashtables[hashtableIndex].put(k, v);
    }

   // get方法
    public V get(K k) {
       // 根据hashcode获取数据存放的index
        int hashtableIndex = k.hashCode() % hashtables.length;
        return hashtables[hashtableIndex].get(k);
    }

   // 测试
    public static void main(String[] args) {

        MyConcurrentHashMap<String, String> concurrentHashMap = new MyConcurrentHashMap<>();
        concurrentHashMap.put("m", "mv");
        concurrentHashMap.put("k", "kv");
        System.out.println(concurrentHashMap.get("m") + "--" + concurrentHashMap.get("k"));
    }
}

6. ConcurrentHashMap如何并发扩容

7. ConcurrentHashMap 1.8 为何取消分段锁

8. ConcurrentHashMap 1.7 为什么使用lock锁而不是synchronized

9. ConcurrentHashMap 1.8 为什么使用synchronized而不是使用lock锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值