从Java并发集合看锁优化策略

来源:互联网 发布:美工钢笔字 编辑:程序博客网 时间:2024/05/03 20:00

1.ConcurrentHashMap

ConcurrentHashMap是支持并发的HashMap类,可以在多线程环境下保证线程安全。ConcurrentHashMap的核心思想就是减小锁的粒度。传统的线程安全的Hashtable在同步时采用了synchronized关键字,所有对Hashtable的读写操作都要先获取对象锁,在锁竞争比较激烈的情况下会导致性能很低。ConcurrentHashMap在此基础上采用和分段的策略,将一个HashMap最多分为16个段(Segment),对不同段上的操作可以并发执行,只有在同一个段上的读写才使用加锁的策略。通过这种减小锁粒度的方式,大大提高了性能。如ConcurrentHashMap的put方法源码如下:

public V put(K key, V value) {        //先找到value所在的段        Segment<K,V> s;        if (value == null)            throw new NullPointerException();        int hash = hash(key);        int j = (hash >>> segmentShift) & segmentMask;        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment            s = ensureSegment(j);        //针对对应的段进行操作        return s.put(key, hash, value, false);    }

ConcurrentHashMap.Segment的put方法如下:

final V put(K key, int hash, V value, boolean onlyIfAbsent) {            HashEntry<K,V> node = tryLock() ? null :                scanAndLockForPut(key, hash, value);            V oldValue;            try {                HashEntry<K,V>[] tab = table;                int index = (tab.length - 1) & hash;                HashEntry<K,V> first = entryAt(tab, index);                for (HashEntry<K,V> e = first;;) {                    if (e != null) {                        K k;                        if ((k = e.key) == key ||                            (e.hash == hash && key.equals(k))) {                            oldValue = e.value;                            if (!onlyIfAbsent) {                                e.value = value;                                ++modCount;                            }                            break;                        }                        e = e.next;                    }                    else {                        if (node != null)                            node.setNext(first);                        else                            node = new HashEntry<K,V>(hash, key, value, first);                        int c = count + 1;                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)                            rehash(node);                        else                            setEntryAt(tab, index, node);                        ++modCount;                        count = c;                        oldValue = null;                        break;                    }                }            } finally {                unlock();            }            return oldValue;        }

可以看出,在对ConcurrentHashMap进行操作时,会先找到数据所在的段,只有在同一个段上的数据才会采用加锁的机制,非同段则可以并发执行。

2.CopyOnWriteArrayList

CopyOnWriteArrayList采用了读、写锁分离的思想。所有对CopyOnWriteArrayList的读操作可以进行无锁的并发执行,当进行写操作时,会先将原数组复制一份再进行修改,修改后将新数组的引用传递给原数组,并释放复制数组所占的空间。CopyOnWriteArrayList非常适合应用于大量读取少量修改的并发场景下。

CopyOnWriteArrayList的get方法如下:

public E get(int index) {        //直接进行随机访问        return get(getArray(), index);    }

add方法实现如下:

 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();        }    }

综上,CopyOnWriteArrayList在读操作时可以进行无锁的并发,在写操作时才进行同步操作,在大量读少量写的场景下可以大大提高效率。

1 0