ConcurrentHashMap-Quellcodeanalyse in JDK7

1. Erweiterung in hashMap

Während der Übertragung können Probleme mit zirkulär verknüpften Listen auftreten

1. Verwendung des Kopfeinsetzens

HashMap ist nicht parallelitätssicher, hashTable ist parallelitätssicher und verwendet „synced“, um die Thread-Sicherheit zu gewährleisten.

Ein Beispiel für die put-Methode, die hashTable Thread-Sicherheit garantiert:

        öffentlicher synchronisierter V-Put (K-Taste, V-Wert) {

void addEntry(int hash, K key, V value, int bucketIndex) {
//如果hashMap大小大于等于阈值并且数据元素不为空扩容       
if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);//计算数组下标位置
        }

        createEntry(hash, key, value, bucketIndex);
    }

//扩容
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];
        //将老数组元素放入新数组
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

//转移老数组元素,双重循环遍历数组和链表
void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

1.1 get() (ALLE)

1.2 das Problem beseitigen

entfernen Der letzte meldet einen Fehler: Ausnahme im Thread "main"java.util.ConcurrentModificationException

Lösung: Rufen Sie die remove-Methode des Iterators auf

Der Grund für den Fehler: Beim Aufruf der Methode map.remove(key) wird modCount ++, sodass beim Aufruf von nextEntry() von hashmap ein Fehler gemeldet wird.

if (modCount!=erwarteterModCount)

        werfe neue ConcurrentModificationException();

Als Lösung wird bei Verwendung der Iterator-Remove-Methode die Hashmap neu zugewiesen

erwartetModCount = modCount;
import java.util.HashMap;
import java.util.Iterator;

public class HashMapTest {

    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("1","1");
        map.put("2","2");

//        for(String key:map.keySet()){
//            if("1".equals(key)){
//                map.remove(key);
//            }
//        }

//        for(String key:map.keySet()){
//            if(key.equals("2")){
//                map.remove(key);//Exception in thread "main" java.util.ConcurrentModificationException
//            }
//        }

        //上述代码编译后代码
        Iterator i$ = map.keySet().iterator();

        while(i$.hasNext()) {
            String key = (String)i$.next();
            if (key.equals("2")) {
//                map.remove(key);//Exception in thread "main" java.util.ConcurrentModificationException
                //解决方法调用iterator的remove方法
                i$.remove();
            }
        }


    }
}

2. jdk7-cocurrentHashMap

cocurrentHashMap übernimmt die Idee der Segmentierung, Segment, das nacheinander aus HashEntry-Arrays zusammengesetzt wird, und Segment erbt reentrantLock, sodass es nur Thread-Sicherheit garantieren kann.

2.1 Bauweise

DEFAULT_INITIAL_CAPACITY: Standard-Anfangsgröße 16
DEFAULT_LOAD_FACTOR: 0.75f, als Berechnungsfaktor!
DEFAULT_CONCURRENCY_LEVEL: Parallelitätsebene, Standard 16
öffentliche ConcurrentHashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        this.segmentShift = 32 - sshift;
        this.segmentMask = ssize - 1;
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        // create segments and segments[0]
        Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }

 2.2 Put-Methode

public V put(K key, V 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);
    }

2.3 Verwenden Sie UNsafe, um Thread-Sicherheitsprobleme zu lösen (TODO)

Guess you like

Origin blog.csdn.net/qq_21575929/article/details/125037039