MST

星途 面试题库

面试题:Java多线程中线程安全的集合类有哪些及原理

在Java多线程编程中,列举出几种线程安全的集合类,并简要阐述它们是如何保证线程安全的。
45.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. Vector
    • 线程安全实现方式Vector的大部分方法(如addgetremove等)都使用synchronized关键字进行同步。这意味着在同一时间,只有一个线程可以访问这些方法,从而保证了线程安全。例如:
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    
  2. Hashtable
    • 线程安全实现方式:类似VectorHashtable的大多数方法(如putgetremove等)也通过synchronized关键字实现同步。这使得在多线程环境下,对Hashtable的操作是线程安全的。例如:
    public synchronized V put(K key, V value) {
        // 检查键是否为null
        if (value == null) {
            throw new NullPointerException();
        }
        // 计算哈希值
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry!= null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        addEntry(hash, key, value, index);
        return null;
    }
    
  3. ConcurrentHashMap
    • 线程安全实现方式
      • 分段锁机制(早期版本):在Java 7及以前版本,ConcurrentHashMap采用分段锁(Segment)机制。它将数据分成多个段(Segment),每个段就像一个小的Hashtable,都有自己的锁。不同段之间的操作可以并发执行,只有访问同一Segment时才会竞争锁,大大提高了并发性能。
      • CAS和 synchronized 结合(Java 8及以后):Java 8中,ConcurrentHashMap摒弃了分段锁机制,采用数组 + 链表/红黑树的数据结构。在更新操作时,使用CAS(Compare - and - Swap)操作来保证对数组元素的原子性更新,对于链表或红黑树节点的操作使用synchronized关键字。例如,在插入元素时,首先使用CAS尝试将元素插入数组,如果失败(说明有其他线程同时在操作),则对该位置的链表或红黑树加synchronized锁进行操作。
  4. CopyOnWriteArrayList
    • 线程安全实现方式CopyOnWriteArrayList采用写时复制的策略。当对列表进行修改(如addremove等操作)时,会先复制一份原数组,在新数组上进行修改操作,然后将原数组引用指向新数组。而读操作(如get等)则直接读取原数组,不涉及锁操作。由于读操作和写操作使用不同的数组,所以读操作是线程安全的,而写操作虽然需要复制数组,但也通过加锁(ReentrantLock)保证了线程安全。例如:
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    
  5. CopyOnWriteArraySet
    • 线程安全实现方式CopyOnWriteArraySet内部是基于CopyOnWriteArrayList实现的。它的线程安全机制和CopyOnWriteArrayList相同,即写时复制。在添加元素时,会调用CopyOnWriteArrayListaddIfAbsent方法,同样是先复制数组,在新数组上添加元素,然后更新数组引用,保证了线程安全。读操作则直接读取原数组,不涉及锁操作,从而实现线程安全。