使用HashMap.keySet作为脏副本

时间:2017-04-28 04:14:06

标签: java multithreading collections garbage-collection

我有一个要求,我必须使用hashMap,其中值可以动态更改,并且只能插入密钥而不删除或更新。但是,键将被多次读取,并且值可能会多次更新。

对于延迟问题,我没有将整个HashMap作为ConcurrentHashMap,在这种情况下写密钥时我会遇到问题。我很好用键来破坏轻微的数据准确性。所以我决定保留自己的密钥副本。下面的代码片段。

package com.test.collections;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

public class CheckKeySet {

    private static Map<String, ConcurrentHashMap<String, String>> testMap = new HashMap<String, ConcurrentHashMap<String, String>>();

    public static void main(String[] args) {
        CheckKeySet trial = new CheckKeySet();
        try {
            trial.doIt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void doIt() throws InterruptedException {
        Thread accessThread1 = new AccessKeySet("Access1");
        Thread writeThread = new WriteValueSet();
        Thread accessThread2 = new AccessKeySet("Access2");
        Thread accessThread3 = new AccessKeySet("Access3");

        writeThread.start();
        Thread.sleep(1000);
        accessThread1.start();
        Thread.sleep(2000);
        accessThread2.start();
        Thread.sleep(4000);
        accessThread3.start();
    }

    private Set<String> getKeySet() {
        return new TreeSet<String>(testMap.keySet());
    }

    class AccessKeySet extends Thread {

        public AccessKeySet(String string) {
            super(string);
        }

        @Override
        public void run() {
            Set<String> keySet = getKeySet();
            System.out.println("###############Trying to print########## " + getName() + " keySet size " + keySet.size());
            for (String s : keySet) {
                System.out.println(s);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class WriteValueSet extends Thread {

        @Override
        public void run() {
            int i = 1;
            for (i = 1; i < 10000; i++) {
                testMap.put("Check-" + i, new ConcurrentHashMap<String, String>());
                System.out.println("Inserted " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

我的问题是,我在上面的实现中使用HashMap.keySet()作为基值创建新的Set Object。 HashMap.keySet()将永远存在于我的代码中。这会影响我在getKeySet()中创建的脏集对象的GC吗?我希望每当它的持有对象符合gc条件时,就可以使用这个新的set对象。因为hashMap.keySet是活着的,所以不应该停止gced

1 个答案:

答案 0 :(得分:0)

如果我错了,请纠正我,但听起来您不希望键或值从testMap收集垃圾,除非您手动删除它们。相反,您只希望对getKeySet()方法返回的密钥集中的密钥进行垃圾回收,如果可能的话,甚至可能不会从集合中明确删除它们。

假设是这种情况,您应该考虑使用weak references。更准确地说,您可以模拟WeakHashSet(根据this answer)并将getKeySet()方法实现更改为:

private Set<String> getKeySet() {
  Set<String> cache = Collections.newSetFromMap(
    new WeakHashMap<String, Boolean>());
  cache.addAll(this.testMap.keySet());
  return cache;
}

此新代码将强引用testMap的所有键复制到弱引用的cache中。因此,每当从testMap中删除元素后垃圾收集器运行时,垃圾收集器将自动从cache中删除相同的元素。