Java 集合的并发映射-空时移除集合

Java 集合的并发映射-空时移除集合,java,concurrency,java.util.concurrent,Java,Concurrency,Java.util.concurrent,我正在尝试用java编写一个线程安全的Map[K,Set[V]]实现 如果将唯一键添加到地图中,则应创建一个新集(并将其添加到) 如果将非唯一密钥添加到地图中,则应将现有密钥集添加到地图中 如果从集合中删除某个值导致集合为空,则应从映射中删除该条目以避免内存泄漏 我想解决这个问题,而不需要同步整个事情 我在下面包含了一个失败的测试用例,如果您有解决方案,请告诉我 package org.deleteme; import java.util.ArrayList; import java.util

我正在尝试用java编写一个线程安全的Map[K,Set[V]]实现

  • 如果将唯一键添加到地图中,则应创建一个新集(并将其添加到)
  • 如果将非唯一密钥添加到地图中,则应将现有密钥集添加到地图中
  • 如果从集合中删除某个值导致集合为空,则应从映射中删除该条目以避免内存泄漏

  • 我想解决这个问题,而不需要同步整个事情
  • 我在下面包含了一个失败的测试用例,如果您有解决方案,请告诉我

    package org.deleteme;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    import junit.framework.Assert;
    
    import org.junit.Test;
    
    public class ConcurrentSetMapTest {
        public static class ConcurrentSetMap<K, V> {
            private final ConcurrentMap<K, Set<V>> map = new ConcurrentHashMap<K, Set<V>>();
    
            public void add(K key, V value) {
                Set<V> set = map.get(key);
                if (set != null) {
                    set.add(value);
                } else {
                    Set<V> candidateSet = createConcurrentSet(value);
                    set = map.putIfAbsent(key, candidateSet);
                    if (set != null) {
                        // candidate set not accepted, use existing
                        set.add(value);
                    }
                }
            }
    
            public void remove(K key, V value) {
                Set<V> set = map.get(key);
                if (set != null) {
                    boolean removed = set.remove(value);
                    if (removed && set.isEmpty()) {
                        // this is not thread-safe and causes the test to fail
                        map.remove(key, set);
                    }
                }
            }
    
            public boolean contains(K key, V value) {
                Set<V> set = map.get(key);
                if (set == null) {
                    return false;
                }
                return set.contains(value);
            }
    
            protected Set<V> createConcurrentSet(V element) {
                Set<V> set = Collections.newSetFromMap(new ConcurrentHashMap<V, Boolean>());
                set.add(element);
                return set;
            }
        }
    
        @Test
        public void testThreadSafe() throws InterruptedException, ExecutionException {
            ConcurrentSetMap<String, String> setMap = new ConcurrentSetMap<String, String>();
            ExecutorService executors = Executors.newFixedThreadPool(4);
            List<Future<?>> futures = new ArrayList<Future<?>>();
    
            futures.add(executors.submit(new TestWorker(setMap, "key1")));
            futures.add(executors.submit(new TestWorker(setMap, "key1")));
            futures.add(executors.submit(new TestWorker(setMap, "key2")));
            futures.add(executors.submit(new TestWorker(setMap, "key2")));
    
            for (Future<?> future : futures) {
                future.get();
            }
        }
    
        public static class TestWorker implements Runnable {
            ConcurrentSetMap<String, String> setMap;
            String key;
    
            public TestWorker(ConcurrentSetMap<String, String> setMap, String key) {
                super();
                this.setMap = setMap;
                this.key = key;
            }
    
            public void run() {
                int sampleSize = 100000;
                for (int i = 0; i < sampleSize; ++ i) {
                    // avoid value clashes with other threads
                    String value = Thread.currentThread().getName() + i;
    
                    Assert.assertFalse("Should not exist before add", setMap.contains(key, value));
                    setMap.add(key, value);
                    Assert.assertTrue("Should exist after add", setMap.contains(key, value));
                    setMap.remove(key, value);
                    Assert.assertFalse("Should not exist after remove", setMap.contains(key, value));
                }
            }
        }
    }
    
    package org.deleteme;
    导入java.util.ArrayList;
    导入java.util.Collections;
    导入java.util.List;
    导入java.util.Set;
    导入java.util.concurrent.ConcurrentHashMap;
    导入java.util.concurrent.ConcurrentMap;
    导入java.util.concurrent.ExecutionException;
    导入java.util.concurrent.ExecutorService;
    导入java.util.concurrent.Executors;
    导入java.util.concurrent.Future;
    导入junit.framework.Assert;
    导入org.junit.Test;
    公共类ConcurrentSetMapTest{
    公共静态类ConcurrentSetMap{
    私有最终ConcurrentMap=新ConcurrentHashMap();
    公共无效添加(K键,V值){
    Set=map.get(键);
    如果(设置!=null){
    增加(价值);
    }否则{
    Set candidateSet=createConcurrentSet(值);
    set=map.putIfAbsent(key,candidateSet);
    如果(设置!=null){
    //未接受候选集,请使用现有
    增加(价值);
    }
    }
    }
    公共无效删除(K键,V值){
    Set=map.get(键);
    如果(设置!=null){
    布尔删除=设置。删除(值);
    if(已删除&set.isEmpty()){
    //这不是线程安全的,会导致测试失败
    地图。移除(键,集合);
    }
    }
    }
    公共布尔包含(K键,V值){
    Set=map.get(键);
    if(set==null){
    返回false;
    }
    返回集合.contains(值);
    }
    受保护集createConcurrentSet(V元素){
    Set=Collections.newSetFromMap(新的ConcurrentHashMap());
    集合。添加(元素);
    返回集;
    }
    }
    @试验
    public void testThreadSafe()引发InterruptedException、ExecutionException{
    ConcurrentSetMap setMap=新的ConcurrentSetMap();
    ExecutorService executors=executors.newFixedThreadPool(4);
    列表>();
    futures.add(executors.submit)(新的TestWorker(setMap,“key1”));
    futures.add(executors.submit)(新的TestWorker(setMap,“key1”));
    futures.add(executors.submit)(新的TestWorker(setMap,“key2”));
    futures.add(executors.submit)(新的TestWorker(setMap,“key2”));
    for(未来:未来){
    future.get();
    }
    }
    公共静态类TestWorker实现Runnable{
    ConcurrentSetMap;
    字符串键;
    公共TestWorker(ConcurrentSetMap setMap,字符串键){
    超级();
    this.setMap=setMap;
    this.key=key;
    }
    公开募捐{
    int sampleSize=100000;
    对于(int i=0;i
    在执行多个操作时,需要使用锁定,这些操作需要是原子操作

    public class SynchronousMultiMap<K, V> {
        private final Map<K, Set<V>> map = new LinkedHashMap<K, Set<V>>();
    
        public synchronized void add(K key, V value) {
            Set<V> set = map.get(key);
            if (set == null)
                map.put(key, set = new LinkedHashSet<V>());
            set.add(value);
        }
    
        public synchronized void remove(K key, V value) {
            Set<V> set = map.get(key);
            if (set == null) return;
            set.remove(value);
            if (set.isEmpty()) map.remove(key);
        }
    
        public synchronized boolean contains(K key, V value) {
            Set<V> set = map.get(key);
            return set != null && set.contains(value);
        }
    
        @Test
        public void testThreadSafe() throws ExecutionException, InterruptedException {
            ExecutorService executors = Executors.newFixedThreadPool(3);
            List<Future<?>> futures = new ArrayList<Future<?>>();
            SynchronousMultiMap<String, Integer> setMap = new SynchronousMultiMap<String, Integer>();
            int sampleSize = 1000000;
    
            String[] keys = "key1,key2,key3,key4".split(",");
            for (int i = 0; i < 3; i++)
                futures.add(executors.submit(new TestWorker(setMap, keys, sampleSize, i)));
    
            executors.shutdown();
            for (Future<?> future : futures) {
                future.get();
            }
        }
    
        static class TestWorker implements Runnable {
            final SynchronousMultiMap<String, Integer> setMap;
            final String[] keys;
            final int sampleSize;
            final int value;
    
            public TestWorker(SynchronousMultiMap<String, Integer> setMap, String[] keys, int sampleSize, int value) {
                super();
                this.setMap = setMap;
                this.keys = keys;
                this.sampleSize = sampleSize;
                this.value = value;
            }
    
            public void run() {
                for (int i = 0; i < sampleSize; i += keys.length) {
                    for (String key : keys) {
                        boolean contains = setMap.contains(key, value);
                        if (contains)
                            Assert.assertFalse("Should not exist before add", contains);
                        setMap.add(key, value);
                        boolean contains2 = setMap.contains(key, value);
                        if (!contains2)
                            Assert.assertTrue("Should exist after add", contains2);
                        setMap.remove(key, value);
                        boolean contains3 = setMap.contains(key, value);
                        if (contains3)
                            Assert.assertFalse("Should not exist after remove", contains3);
                    }
                }
            }
        }
    }
    
    公共类同步多重映射{
    私有最终映射=新LinkedHashMap();
    公共同步作废添加(K键,V值){
    Set=map.get(键);
    if(set==null)
    put(key,set=newlinkedhashset());
    增加(价值);
    }
    公共同步无效删除(K键,V值){
    Set=map.get(键);
    if(set==null)返回;
    设置。删除(值);
    if(set.isEmpty())map.remove(key);
    }
    公共同步布尔包含(K键,V值){
    Set=map.get(键);
    返回集!=null&&set.contains(值);
    }
    @试验
    public void testThreadSafe()引发ExecutionException、InterruptedException{
    ExecutorService executors=executors.newFixedThreadPool(3);
    列表>();
    SynchronousMultiMap setMap=新的SynchronousMultiMap();
    int sampleSize=1000000;
    字符串[]keys=“key1,key2,key3,key4”。拆分(“,”;
    对于(int i=0;i<3;i++)
    futures.add(executors.submit(newtestworker(setMap,key,sampleSize,i));
    executors.shutdown();
    for(未来:未来){
    future.get();
    }
    }
    静态类TestWorker实现Runnable{
    最终同步多重映射集映射;
    最终字符串[]键;
    最终int样本化;
    最终整数值;
    公共TestWorker(SynchronousMultiMap setMap、字符串[]键、int-sampleSize、int-value){
    超级();
    this.setMap=setMap;
    this.keys=keys;
    this.sampleSize=sampleSize;
    这个值=值;
    }
    公开募捐{
    for(int i=0;ipublic class ConcurrentSetMap<K,V> {
    
      private final ConcurrentMap<K, Set<V>> _map = new ConcurrentHashMap<K, Set<V>>();
    
      public void add(K key, V value) {
        Set<V> curSet = _map.get(key);
        while(true) {
    
          if((curSet != null) && curSet.contains(value)) {
            break;
          }
    
          Set<V> newSet = new HashSet<V>();
          newSet.add(value);
    
          if(curSet == null) {
    
            curSet = _map.putIfAbsent(key, newSet);
            if(curSet != null) {
              continue;
            }
    
          } else {
    
            newSet.addAll(curSet);
            if(!_map.replace(key, curSet, newSet)) {
              curSet = _map.get(key);
              continue;
            }
          }
    
          break;
        }
      }
    
      public void remove(K key, V value) {
        Set<V> curSet = _map.get(key);
    
        while(true) {
          if((curSet == null) || !curSet.contains(value))  {
            break;
          }
    
          if(curSet.size() == 1) {
    
            if(!_map.remove(key, curSet)) {
              curSet = _map.get(key);
              continue;
            }
    
          } else {
    
            Set<V> newSet = new HashSet<V>();
            newSet.addAll(curSet);
            newSet.remove(value);
            if(!_map.replace(key, curSet, newSet)) {
              curSet = _map.get(key);
              continue;
            }
          }
    
          break;
        }
      }
    
      public boolean contains(K key, V value) {
        Set<V> set = _map.get(key);
        return set != null && set.contains(value);
      }
    }
    
    public static class ConcurrentSetMap<K, V> {
        private final ConcurrentMap<K, Set<V>> map = new ConcurrentHashMap<K, Set<V>>();
    
        public synchronized void add(K key, V value) {
            Set<V> set = map.get(key);
            if (set != null) {
                set.add(value);
            } else {
                map.put(key, createConcurrentSet(value));
            }
        }
    
        public synchronized void remove(K key, V value) {
            Set<V> set = map.get(key);
            if (set != null) {
                set.remove(value);
                if (set.isEmpty()) {
                    map.remove(key);
                }
            }
        }
    
        public boolean contains(K key, V value) {
            return get(key).contains(value);
        }
    
        public Set<V> get(K key) {
            Set<V> set = map.get(key);
            return set == null ? Collections.<V> emptySet() : set;
        }
    
        protected Set<V> createConcurrentSet(V value) {
            Set<V> set = Collections.newSetFromMap(new ConcurrentHashMap<V, Boolean>());
            set.add(value);
            return set;
        }
    }