From caf09712a98c2d2bc4c2f94ee29fed84b5fa4179 Mon Sep 17 00:00:00 2001 From: godotg Date: Sat, 8 Oct 2022 10:58:34 +0800 Subject: [PATCH] feat[concurrent]: add concurrent primitive type CopyOnWriteHashMapLongObject --- .../concurrent/ConcurrentHashSet.java | 10 +- .../CopyOnWriteHashMapLongObject.java | 133 ++++++++++++++++++ .../protocol/collection/ConcurrentTest.java | 64 +++++++++ 3 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 protocol/src/main/java/com/zfoo/protocol/collection/concurrent/CopyOnWriteHashMapLongObject.java create mode 100644 protocol/src/test/java/com/zfoo/protocol/collection/ConcurrentTest.java diff --git a/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/ConcurrentHashSet.java b/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/ConcurrentHashSet.java index f4a61deb..816f135a 100644 --- a/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/ConcurrentHashSet.java +++ b/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/ConcurrentHashSet.java @@ -23,15 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ConcurrentHashSet extends AbstractSet { - private final Map map; - - public ConcurrentHashSet() { - this.map = new ConcurrentHashMap<>(); - } - - public ConcurrentHashSet(int initialCapacity) { - this.map = new ConcurrentHashMap<>(initialCapacity); - } + private final Map map= new ConcurrentHashMap<>(); @Override public Iterator iterator() { diff --git a/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/CopyOnWriteHashMapLongObject.java b/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/CopyOnWriteHashMapLongObject.java new file mode 100644 index 00000000..7ceaf9ed --- /dev/null +++ b/protocol/src/main/java/com/zfoo/protocol/collection/concurrent/CopyOnWriteHashMapLongObject.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.collection.concurrent; + +import io.netty.util.collection.LongObjectHashMap; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author godotg + * @version 3.0 + */ +public class CopyOnWriteHashMapLongObject implements Map { + + private final ReentrantLock lock = new ReentrantLock(); + private volatile LongObjectHashMap map = new LongObjectHashMap<>(); + + private LongObjectHashMap newCopyMap() { + var newMap = new LongObjectHashMap(); + newMap.putAll(map); + return newMap; + } + + private void setNewMap(LongObjectHashMap newMap) { + map = newMap; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public V get(Object key) { + return map.get(key); + } + + public V getPrimitive(long key) { + return map.get(key); + } + + @Override + public V put(Long key, V value) { + lock.lock(); + try { + var newMap = newCopyMap(); + var oldValue = newMap.put(key, value); + setNewMap(newMap); + return oldValue; + } finally { + lock.unlock(); + } + } + + @Override + public V remove(Object key) { + lock.lock(); + try { + var newMap = newCopyMap(); + var oldValue = newMap.remove(key); + setNewMap(newMap); + return oldValue; + } finally { + lock.unlock(); + } + } + + @Override + public void putAll(Map m) { + lock.lock(); + try { + var newMap = newCopyMap(); + newMap.putAll(m); + setNewMap(newMap); + } finally { + lock.unlock(); + } + } + + @Override + public void clear() { + lock.lock(); + try { + var newMap = newCopyMap(); + setNewMap(newMap); + } finally { + lock.unlock(); + } + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } +} diff --git a/protocol/src/test/java/com/zfoo/protocol/collection/ConcurrentTest.java b/protocol/src/test/java/com/zfoo/protocol/collection/ConcurrentTest.java new file mode 100644 index 00000000..09d7db03 --- /dev/null +++ b/protocol/src/test/java/com/zfoo/protocol/collection/ConcurrentTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.protocol.collection; + +import com.zfoo.protocol.collection.concurrent.CopyOnWriteHashMapLongObject; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; + +/** + * @author godotg + * @version 3.0 + */ +@Ignore +public class ConcurrentTest { + + @Test + public void test() throws InterruptedException { + var map = new CopyOnWriteHashMapLongObject(); + var num = 1_0000; + var executorSize = Runtime.getRuntime().availableProcessors(); + var countDownLatch = new CountDownLatch(executorSize); + for (var i = 0; i < executorSize; i++) { + new Thread(new Runnable() { + @Override + public void run() { + for (int j = 0; j < num; j++) { + map.put((long) j, j); + } + countDownLatch.countDown(); + } + }).start(); + } + countDownLatch.await(); + Assert.assertEquals(map.size(), num); + + var countDownLatch2 = new CountDownLatch(executorSize); + for (var i = 0; i < executorSize; i++) { + new Thread(new Runnable() { + @Override + public void run() { + for (int j = 0; j < num; j++) { + map.remove((long) j); + } + countDownLatch2.countDown(); + } + }).start(); + } + countDownLatch2.await(); + Assert.assertTrue(map.isEmpty()); + } +}