2、HashMap線程安全問題

1、併發測試,會出現的賦值成功,但是結果卻是數量少了。

                 new Thread(new Runnable() {
                          @Override
                          public void run() {
                                  for (int i = 0; i < 1000; i++) {
                                           map.put("aaa_"+i, "value_aaa_"+i);
                                           System.out.println(Thread.currentThread().getName() + "put");
                                  }
                          }
                 },"aaa").start();
                 new Thread(new Runnable() {
                          @Override
                          public void run() {
                                  for (int i = 0; i < 1000; i++) {
                                           map.put("bbb_"+i, "value_bbb_"+i);
                                           System.out.println(Thread.currentThread().getName() + "put");
                                  }
                          }
                 },"bbb").start();;
                 new Thread(new Runnable() {
                          @Override
                          public void run() {
                                  for (int i = 0; i < 1000; i++) {
                                           map.put("ccc_"+i, "value_ccc_"+i);
                                           System.out.println(Thread.currentThread().getName() + "put");
                                  }
                          }
                 },"ccc").start();;
                 Set<Entry<String,Object>> entrySet = map.entrySet();  // 怎麼做到返回所有的映射關係
                 try {
                          Thread.sleep(3000);
                 } catch (InterruptedException e) {
                          // TODO Auto-generated catch block
                          e.printStackTrace();
                 }
                 int size = entrySet.size();
                 System.out.println(size); // 映射的數目纔是hashmap的存放的鍵值對


// 打印結果,會出現size小於3000的情況。


hashmap就是共享資源
分析:
多線程訪問,在創建entry時候,拿到的Entry<K,V> e = table[i] ,那麼再new Entry的時候就會後一個線程覆蓋上一個線程的值。
(原理:多個線程同時發生碰撞的時候,且都在數組的同一個位置時。同時對鏈表的頭部更新操作,出現值被覆蓋)
 void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

2、線程不安全造成環狀鏈表分析
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; // 拿到next,保存舊鏈表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.next (第一次table[i]null)
                newTable[i] = e; // 再把e賦給newTable[i]
每次循環都是將新來的e指向已經存在的對象newTable[i]處的鏈表,再將此鏈表賦給newTable[i]。這樣做和put原理一樣,複雜度是O(1)
                e = next;
            }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章