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



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