[Fix] Fixed concurrent modification exception in SynchronizedCollections

This commit is contained in:
Robert von Burg 2020-05-19 11:16:53 +02:00
parent af8ac81a18
commit a0b24f74a5
4 changed files with 555 additions and 73 deletions

View File

@ -29,7 +29,7 @@ public class SynchronizedCollections {
SynchronizedMapOfLists(MapOfLists<T, U> m) { SynchronizedMapOfLists(MapOfLists<T, U> m) {
this.m = m; this.m = m;
this.mutex = new Object(); this.mutex = this;
} }
@Override @Override
@ -174,7 +174,7 @@ public class SynchronizedCollections {
SynchronizedMapOfMaps(MapOfMaps<T, U, V> m) { SynchronizedMapOfMaps(MapOfMaps<T, U, V> m) {
this.m = m; this.m = m;
this.mutex = new Object(); this.mutex = this;
} }
@Override @Override
@ -341,7 +341,7 @@ public class SynchronizedCollections {
public SynchronizedMapOfSets(MapOfSets<T, U> m) { public SynchronizedMapOfSets(MapOfSets<T, U> m) {
this.m = m; this.m = m;
this.mutex = new Object(); this.mutex = this;
} }
@Override @Override
@ -507,35 +507,35 @@ public class SynchronizedCollections {
@Override @Override
public int size() { public int size() {
synchronized (mutex) { synchronized (this.mutex) {
return c.size(); return c.size();
} }
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
synchronized (mutex) { synchronized (this.mutex) {
return c.isEmpty(); return c.isEmpty();
} }
} }
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
synchronized (mutex) { synchronized (this.mutex) {
return c.contains(o); return c.contains(o);
} }
} }
@Override @Override
public Object[] toArray() { public Object[] toArray() {
synchronized (mutex) { synchronized (this.mutex) {
return c.toArray(); return c.toArray();
} }
} }
@Override @Override
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
synchronized (mutex) { synchronized (this.mutex) {
return c.toArray(a); return c.toArray(a);
} }
} }
@ -547,70 +547,70 @@ public class SynchronizedCollections {
@Override @Override
public boolean add(E e) { public boolean add(E e) {
synchronized (mutex) { synchronized (this.mutex) {
return c.add(e); return c.add(e);
} }
} }
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
synchronized (mutex) { synchronized (this.mutex) {
return c.remove(o); return c.remove(o);
} }
} }
@Override @Override
public boolean containsAll(Collection<?> coll) { public boolean containsAll(Collection<?> coll) {
synchronized (mutex) { synchronized (this.mutex) {
return c.containsAll(coll); return c.containsAll(coll);
} }
} }
@Override @Override
public boolean addAll(Collection<? extends E> coll) { public boolean addAll(Collection<? extends E> coll) {
synchronized (mutex) { synchronized (this.mutex) {
return c.addAll(coll); return c.addAll(coll);
} }
} }
@Override @Override
public boolean removeAll(Collection<?> coll) { public boolean removeAll(Collection<?> coll) {
synchronized (mutex) { synchronized (this.mutex) {
return c.removeAll(coll); return c.removeAll(coll);
} }
} }
@Override @Override
public boolean retainAll(Collection<?> coll) { public boolean retainAll(Collection<?> coll) {
synchronized (mutex) { synchronized (this.mutex) {
return c.retainAll(coll); return c.retainAll(coll);
} }
} }
@Override @Override
public void clear() { public void clear() {
synchronized (mutex) { synchronized (this.mutex) {
c.clear(); c.clear();
} }
} }
@Override @Override
public String toString() { public String toString() {
synchronized (mutex) { synchronized (this.mutex) {
return c.toString(); return c.toString();
} }
} }
@Override @Override
public void forEach(Consumer<? super E> consumer) { public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) { synchronized (this.mutex) {
c.forEach(consumer); c.forEach(consumer);
} }
} }
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) { synchronized (this.mutex) {
return c.removeIf(filter); return c.removeIf(filter);
} }
} }
@ -631,7 +631,7 @@ public class SynchronizedCollections {
} }
private void writeObject(ObjectOutputStream s) throws IOException { private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) { synchronized (this.mutex) {
s.defaultWriteObject(); s.defaultWriteObject();
} }
} }
@ -650,63 +650,63 @@ public class SynchronizedCollections {
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)
return true; return true;
synchronized (mutex) { synchronized (this.mutex) {
return list.equals(o); return list.equals(o);
} }
} }
@Override @Override
public int hashCode() { public int hashCode() {
synchronized (mutex) { synchronized (this.mutex) {
return list.hashCode(); return list.hashCode();
} }
} }
@Override @Override
public E get(int index) { public E get(int index) {
synchronized (mutex) { synchronized (this.mutex) {
return list.get(index); return list.get(index);
} }
} }
@Override @Override
public E set(int index, E element) { public E set(int index, E element) {
synchronized (mutex) { synchronized (this.mutex) {
return list.set(index, element); return list.set(index, element);
} }
} }
@Override @Override
public void add(int index, E element) { public void add(int index, E element) {
synchronized (mutex) { synchronized (this.mutex) {
list.add(index, element); list.add(index, element);
} }
} }
@Override @Override
public E remove(int index) { public E remove(int index) {
synchronized (mutex) { synchronized (this.mutex) {
return list.remove(index); return list.remove(index);
} }
} }
@Override @Override
public int indexOf(Object o) { public int indexOf(Object o) {
synchronized (mutex) { synchronized (this.mutex) {
return list.indexOf(o); return list.indexOf(o);
} }
} }
@Override @Override
public int lastIndexOf(Object o) { public int lastIndexOf(Object o) {
synchronized (mutex) { synchronized (this.mutex) {
return list.lastIndexOf(o); return list.lastIndexOf(o);
} }
} }
@Override @Override
public boolean addAll(int index, Collection<? extends E> c) { public boolean addAll(int index, Collection<? extends E> c) {
synchronized (mutex) { synchronized (this.mutex) {
return list.addAll(index, c); return list.addAll(index, c);
} }
} }
@ -723,21 +723,21 @@ public class SynchronizedCollections {
@Override @Override
public List<E> subList(int fromIndex, int toIndex) { public List<E> subList(int fromIndex, int toIndex) {
synchronized (mutex) { synchronized (this.mutex) {
return new SynchronizedList<>(list.subList(fromIndex, toIndex), mutex); return new SynchronizedList<>(list.subList(fromIndex, toIndex), this.mutex);
} }
} }
@Override @Override
public void replaceAll(UnaryOperator<E> operator) { public void replaceAll(UnaryOperator<E> operator) {
synchronized (mutex) { synchronized (this.mutex) {
list.replaceAll(operator); list.replaceAll(operator);
} }
} }
@Override @Override
public void sort(Comparator<? super E> c) { public void sort(Comparator<? super E> c) {
synchronized (mutex) { synchronized (this.mutex) {
list.sort(c); list.sort(c);
} }
} }
@ -753,14 +753,14 @@ public class SynchronizedCollections {
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)
return true; return true;
synchronized (mutex) { synchronized (this.mutex) {
return c.equals(o); return c.equals(o);
} }
} }
@Override @Override
public int hashCode() { public int hashCode() {
synchronized (mutex) { synchronized (this.mutex) {
return c.hashCode(); return c.hashCode();
} }
} }
@ -777,42 +777,42 @@ public class SynchronizedCollections {
@Override @Override
public Comparator<? super E> comparator() { public Comparator<? super E> comparator() {
synchronized (mutex) { synchronized (this.mutex) {
return ss.comparator(); return ss.comparator();
} }
} }
@Override @Override
public SortedSet<E> subSet(E fromElement, E toElement) { public SortedSet<E> subSet(E fromElement, E toElement) {
synchronized (mutex) { synchronized (this.mutex) {
return new SynchronizedSortedSet<>(ss.subSet(fromElement, toElement), mutex); return new SynchronizedSortedSet<>(ss.subSet(fromElement, toElement), this.mutex);
} }
} }
@Override @Override
public SortedSet<E> headSet(E toElement) { public SortedSet<E> headSet(E toElement) {
synchronized (mutex) { synchronized (this.mutex) {
return new SynchronizedSortedSet<>(ss.headSet(toElement), mutex); return new SynchronizedSortedSet<>(ss.headSet(toElement), this.mutex);
} }
} }
@Override @Override
public SortedSet<E> tailSet(E fromElement) { public SortedSet<E> tailSet(E fromElement) {
synchronized (mutex) { synchronized (this.mutex) {
return new SynchronizedSortedSet<>(ss.tailSet(fromElement), mutex); return new SynchronizedSortedSet<>(ss.tailSet(fromElement), this.mutex);
} }
} }
@Override @Override
public E first() { public E first() {
synchronized (mutex) { synchronized (this.mutex) {
return ss.first(); return ss.first();
} }
} }
@Override @Override
public E last() { public E last() {
synchronized (mutex) { synchronized (this.mutex) {
return ss.last(); return ss.last();
} }
} }
@ -829,55 +829,55 @@ public class SynchronizedCollections {
} }
public int size() { public int size() {
synchronized (mutex) { synchronized (this.mutex) {
return m.size(); return m.size();
} }
} }
public boolean isEmpty() { public boolean isEmpty() {
synchronized (mutex) { synchronized (this.mutex) {
return m.isEmpty(); return m.isEmpty();
} }
} }
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
synchronized (mutex) { synchronized (this.mutex) {
return m.containsKey(key); return m.containsKey(key);
} }
} }
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
synchronized (mutex) { synchronized (this.mutex) {
return m.containsValue(value); return m.containsValue(value);
} }
} }
public V get(Object key) { public V get(Object key) {
synchronized (mutex) { synchronized (this.mutex) {
return m.get(key); return m.get(key);
} }
} }
public V put(K key, V value) { public V put(K key, V value) {
synchronized (mutex) { synchronized (this.mutex) {
return m.put(key, value); return m.put(key, value);
} }
} }
public V remove(Object key) { public V remove(Object key) {
synchronized (mutex) { synchronized (this.mutex) {
return m.remove(key); return m.remove(key);
} }
} }
public void putAll(Map<? extends K, ? extends V> map) { public void putAll(Map<? extends K, ? extends V> map) {
synchronized (mutex) { synchronized (this.mutex) {
m.putAll(map); m.putAll(map);
} }
} }
public void clear() { public void clear() {
synchronized (mutex) { synchronized (this.mutex) {
m.clear(); m.clear();
} }
} }
@ -887,25 +887,25 @@ public class SynchronizedCollections {
private transient Collection<V> values; private transient Collection<V> values;
public Set<K> keySet() { public Set<K> keySet() {
synchronized (mutex) { synchronized (this.mutex) {
if (keySet == null) if (keySet == null)
keySet = new SynchronizedSet<>(m.keySet(), mutex); keySet = new SynchronizedSet<>(m.keySet(), this.mutex);
return keySet; return keySet;
} }
} }
public Set<Map.Entry<K, V>> entrySet() { public Set<Map.Entry<K, V>> entrySet() {
synchronized (mutex) { synchronized (this.mutex) {
if (entrySet == null) if (entrySet == null)
entrySet = new SynchronizedSet<>(m.entrySet(), mutex); entrySet = new SynchronizedSet<>(m.entrySet(), this.mutex);
return entrySet; return entrySet;
} }
} }
public Collection<V> values() { public Collection<V> values() {
synchronized (mutex) { synchronized (this.mutex) {
if (values == null) if (values == null)
values = new SynchronizedCollection<>(m.values(), mutex); values = new SynchronizedCollection<>(m.values(), this.mutex);
return values; return values;
} }
} }
@ -913,102 +913,102 @@ public class SynchronizedCollections {
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)
return true; return true;
synchronized (mutex) { synchronized (this.mutex) {
return m.equals(o); return m.equals(o);
} }
} }
public int hashCode() { public int hashCode() {
synchronized (mutex) { synchronized (this.mutex) {
return m.hashCode(); return m.hashCode();
} }
} }
public String toString() { public String toString() {
synchronized (mutex) { synchronized (this.mutex) {
return m.toString(); return m.toString();
} }
} }
@Override @Override
public V getOrDefault(Object k, V defaultValue) { public V getOrDefault(Object k, V defaultValue) {
synchronized (mutex) { synchronized (this.mutex) {
return m.getOrDefault(k, defaultValue); return m.getOrDefault(k, defaultValue);
} }
} }
@Override @Override
public void forEach(BiConsumer<? super K, ? super V> action) { public void forEach(BiConsumer<? super K, ? super V> action) {
synchronized (mutex) { synchronized (this.mutex) {
m.forEach(action); m.forEach(action);
} }
} }
@Override @Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
synchronized (mutex) { synchronized (this.mutex) {
m.replaceAll(function); m.replaceAll(function);
} }
} }
@Override @Override
public V putIfAbsent(K key, V value) { public V putIfAbsent(K key, V value) {
synchronized (mutex) { synchronized (this.mutex) {
return m.putIfAbsent(key, value); return m.putIfAbsent(key, value);
} }
} }
@Override @Override
public boolean remove(Object key, Object value) { public boolean remove(Object key, Object value) {
synchronized (mutex) { synchronized (this.mutex) {
return m.remove(key, value); return m.remove(key, value);
} }
} }
@Override @Override
public boolean replace(K key, V oldValue, V newValue) { public boolean replace(K key, V oldValue, V newValue) {
synchronized (mutex) { synchronized (this.mutex) {
return m.replace(key, oldValue, newValue); return m.replace(key, oldValue, newValue);
} }
} }
@Override @Override
public V replace(K key, V value) { public V replace(K key, V value) {
synchronized (mutex) { synchronized (this.mutex) {
return m.replace(key, value); return m.replace(key, value);
} }
} }
@Override @Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
synchronized (mutex) { synchronized (this.mutex) {
return m.computeIfAbsent(key, mappingFunction); return m.computeIfAbsent(key, mappingFunction);
} }
} }
@Override @Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
synchronized (mutex) { synchronized (this.mutex) {
return m.computeIfPresent(key, remappingFunction); return m.computeIfPresent(key, remappingFunction);
} }
} }
@Override @Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
synchronized (mutex) { synchronized (this.mutex) {
return m.compute(key, remappingFunction); return m.compute(key, remappingFunction);
} }
} }
@Override @Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
synchronized (mutex) { synchronized (this.mutex) {
return m.merge(key, value, remappingFunction); return m.merge(key, value, remappingFunction);
} }
} }
private void writeObject(ObjectOutputStream s) throws IOException { private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) { synchronized (this.mutex) {
s.defaultWriteObject(); s.defaultWriteObject();
} }
} }

View File

@ -0,0 +1,160 @@
package li.strolch.utils.collections;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfLists;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SynchronizedMapOfListsTest {
private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfListsTest.class);
private ExecutorService executorService;
@Before
public void before() {
this.executorService = Executors.newCachedThreadPool();
}
@Test
public void shouldForEach() throws ExecutionException, InterruptedException {
MapOfLists<String, String> mapOfLists = buildMapOfLists();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfLists, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
mapOfLists.forEach((s, list) -> list.forEach(s1 -> logger.info(s + " " + s1)));
mapOfLists.getList("Resource").forEach(s1 -> logger.info(" " + s1));
}
return true;
};
runTest(addTask, iterateTask, run);
}
@Test
public void shouldIterate() throws ExecutionException, InterruptedException {
MapOfLists<String, String> mapOfLists = buildMapOfLists();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfLists, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
Set<String> types = mapOfLists.keySet();
for (String type : types) {
synchronized (mapOfLists) {
for (String id : mapOfLists.getList(type)) {
logger.info(type + " " + id);
}
}
}
synchronized (mapOfLists) {
List<String> resources = mapOfLists.getList("Resource");
for (String value : resources) {
logger.info("Resource: value: " + value);
}
}
}
return true;
};
runTest(addTask, iterateTask, run);
}
private void runTest(Callable<Boolean> addTask, Callable<Boolean> iterateTask, AtomicBoolean run)
throws InterruptedException, ExecutionException {
Future<Boolean> task0 = this.executorService.submit(addTask);
Future<Boolean> task1 = this.executorService.submit(iterateTask);
Future<Boolean> task2 = this.executorService.submit(iterateTask);
Future<Boolean> task3 = this.executorService.submit(iterateTask);
Future<Boolean> task4 = this.executorService.submit(iterateTask);
Future<Boolean> task5 = this.executorService.submit(iterateTask);
run.set(true);
Thread.sleep(1000L);
run.set(false);
Boolean result0 = task0.get();
Boolean result1 = task1.get();
Boolean result2 = task2.get();
Boolean result3 = task3.get();
Boolean result4 = task4.get();
Boolean result5 = task5.get();
assertTrue(result0);
assertTrue(result1);
assertTrue(result2);
assertTrue(result3);
assertTrue(result4);
assertTrue(result5);
}
private Boolean addToMap(MapOfLists<String, String> mapOfLists, AtomicBoolean run) {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
addElement(mapOfLists, "Resource", UUID.randomUUID().toString());
addElement(mapOfLists, "Resource", UUID.randomUUID().toString());
addElement(mapOfLists, "Order", UUID.randomUUID().toString());
addElement(mapOfLists, "Order", UUID.randomUUID().toString());
addElement(mapOfLists, "Order", UUID.randomUUID().toString());
addElement(mapOfLists, "Activity", UUID.randomUUID().toString());
addElement(mapOfLists, "Activity", UUID.randomUUID().toString());
mapOfLists.removeElement("Resource", "Ball");
mapOfLists.removeElement("Order", "ToStock");
mapOfLists.removeElement("Activity", "ToStock");
}
return true;
}
private void addElement(MapOfLists<String, String> mapOfLists, String type, String id) {
logger.info("Adding " + type + " " + id);
mapOfLists.addElement(type, id);
}
private MapOfLists<String, String> buildMapOfLists() {
MapOfLists<String, String> mapOfLists = synchronizedMapOfLists(new MapOfLists<>(true));
mapOfLists.addElement("Resource", "Ball");
mapOfLists.addElement("Resource", "Car");
mapOfLists.addElement("Order", "StockOrder");
mapOfLists.addElement("Order", "ToStock");
mapOfLists.addElement("Order", "FromStock");
mapOfLists.addElement("Activity", "FromStock");
mapOfLists.addElement("Activity", "ToStock");
assertEquals(Arrays.asList("Ball", "Car"), mapOfLists.getList("Resource"));
assertNull(mapOfLists.getList("xxx"));
return mapOfLists;
}
}

View File

@ -0,0 +1,162 @@
package li.strolch.utils.collections;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfMaps;
import static org.junit.Assert.*;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SynchronizedMapOfMapsTest {
private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfMapsTest.class);
private ExecutorService executorService;
@Before
public void before() {
this.executorService = Executors.newCachedThreadPool();
}
@Test
public void shouldForEach() throws ExecutionException, InterruptedException {
MapOfMaps<String, String, String> mapOfMaps = buildMapOfMaps();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfMaps, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
mapOfMaps.forEach(
(s, subTypeMap) -> subTypeMap.forEach((s1, s2) -> logger.info(s + " " + s1 + " " + s2)));
mapOfMaps.getMap("Resource").forEach((s1, s2) -> logger.info(" " + s1 + " " + s2));
}
return true;
};
runTest(addTask, iterateTask, run);
}
@Test
public void shouldIterate() throws ExecutionException, InterruptedException {
MapOfMaps<String, String, String> mapOfMaps = buildMapOfMaps();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfMaps, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
Set<String> types = mapOfMaps.keySet();
for (String type : types) {
Map<String, String> subTypeMap = mapOfMaps.getMap(type);
Set<String> subTypes = subTypeMap.keySet();
synchronized (mapOfMaps) {
for (String subType : subTypes) {
logger.info(type + " " + subType + " " + mapOfMaps.getElement(type, subType));
}
}
}
synchronized (mapOfMaps) {
Map<String, String> resources = mapOfMaps.getMap("Resource");
for (String value : resources.values()) {
logger.info("Resource: value: " + value);
}
}
}
return true;
};
runTest(addTask, iterateTask, run);
}
private void runTest(Callable<Boolean> addTask, Callable<Boolean> iterateTask, AtomicBoolean run)
throws InterruptedException, ExecutionException {
Future<Boolean> task0 = this.executorService.submit(addTask);
Future<Boolean> task1 = this.executorService.submit(iterateTask);
Future<Boolean> task2 = this.executorService.submit(iterateTask);
Future<Boolean> task3 = this.executorService.submit(iterateTask);
Future<Boolean> task4 = this.executorService.submit(iterateTask);
Future<Boolean> task5 = this.executorService.submit(iterateTask);
run.set(true);
Thread.sleep(1000L);
run.set(false);
Boolean result0 = task0.get();
Boolean result1 = task1.get();
Boolean result2 = task2.get();
Boolean result3 = task3.get();
Boolean result4 = task4.get();
Boolean result5 = task5.get();
assertTrue(result0);
assertTrue(result1);
assertTrue(result2);
assertTrue(result3);
assertTrue(result4);
assertTrue(result5);
}
private Boolean addToMap(MapOfMaps<String, String, String> mapOfMaps, AtomicBoolean run) {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
addElement(mapOfMaps, "Resource", "Ball", UUID.randomUUID().toString());
addElement(mapOfMaps, "Resource", "Car", UUID.randomUUID().toString());
addElement(mapOfMaps, "Order", "StockOrder", UUID.randomUUID().toString());
addElement(mapOfMaps, "Order", "ToStock", UUID.randomUUID().toString());
addElement(mapOfMaps, "Order", "FromStock", UUID.randomUUID().toString());
addElement(mapOfMaps, "Activity", "FromStock", UUID.randomUUID().toString());
addElement(mapOfMaps, "Activity", "ToStock", UUID.randomUUID().toString());
mapOfMaps.removeElement("Resource", "Ball");
mapOfMaps.removeElement("Order", "ToStock");
mapOfMaps.removeElement("Activity", "ToStock");
}
return true;
}
private void addElement(MapOfMaps<String, String, String> mapOfMaps, String type, String subType, String id) {
logger.info("Adding " + type + " " + subType + " " + id);
mapOfMaps.addElement(type, subType, id);
}
private MapOfMaps<String, String, String> buildMapOfMaps() {
MapOfMaps<String, String, String> mapOfMaps = synchronizedMapOfMaps(new MapOfMaps<>(true));
mapOfMaps.addElement("Resource", "Ball", "yellow");
mapOfMaps.addElement("Resource", "Car", "car1");
mapOfMaps.addElement("Order", "StockOrder", "stockOrder1");
mapOfMaps.addElement("Order", "ToStock", "toStock1");
mapOfMaps.addElement("Order", "FromStock", "fromStock1");
mapOfMaps.addElement("Activity", "FromStock", "fromStock1");
mapOfMaps.addElement("Activity", "ToStock", "toStock1");
assertEquals("yellow", mapOfMaps.getElement("Resource", "Ball"));
assertNull(mapOfMaps.getMap("xxx"));
return mapOfMaps;
}
}

View File

@ -0,0 +1,160 @@
package li.strolch.utils.collections;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfSets;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SynchronizedMapOfSetsTest {
private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfSetsTest.class);
private ExecutorService executorService;
@Before
public void before() {
this.executorService = Executors.newCachedThreadPool();
}
@Test
public void shouldForEach() throws ExecutionException, InterruptedException {
MapOfSets<String, String> mapOfSets = buildMapOfSets();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfSets, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
mapOfSets.forEach((s, list) -> list.forEach(s1 -> logger.info(s + " " + s1)));
mapOfSets.getSet("Resource").forEach(s1 -> logger.info(" " + s1));
}
return true;
};
runTest(addTask, iterateTask, run);
}
@Test
public void shouldIterate() throws ExecutionException, InterruptedException {
MapOfSets<String, String> mapOfSets = buildMapOfSets();
AtomicBoolean run = new AtomicBoolean(false);
Callable<Boolean> addTask = () -> addToMap(mapOfSets, run);
Callable<Boolean> iterateTask = () -> {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
Set<String> types = mapOfSets.keySet();
for (String type : types) {
synchronized (mapOfSets) {
for (String id : mapOfSets.getSet(type)) {
logger.info(type + " " + id);
}
}
}
synchronized (mapOfSets) {
Set<String> resources = mapOfSets.getSet("Resource");
for (String value : resources) {
logger.info("Resource: value: " + value);
}
}
}
return true;
};
runTest(addTask, iterateTask, run);
}
private void runTest(Callable<Boolean> addTask, Callable<Boolean> iterateTask, AtomicBoolean run)
throws InterruptedException, ExecutionException {
Future<Boolean> task0 = this.executorService.submit(addTask);
Future<Boolean> task1 = this.executorService.submit(iterateTask);
Future<Boolean> task2 = this.executorService.submit(iterateTask);
Future<Boolean> task3 = this.executorService.submit(iterateTask);
Future<Boolean> task4 = this.executorService.submit(iterateTask);
Future<Boolean> task5 = this.executorService.submit(iterateTask);
run.set(true);
Thread.sleep(1000L);
run.set(false);
Boolean result0 = task0.get();
Boolean result1 = task1.get();
Boolean result2 = task2.get();
Boolean result3 = task3.get();
Boolean result4 = task4.get();
Boolean result5 = task5.get();
assertTrue(result0);
assertTrue(result1);
assertTrue(result2);
assertTrue(result3);
assertTrue(result4);
assertTrue(result5);
}
private Boolean addToMap(MapOfSets<String, String> mapOfSets, AtomicBoolean run) {
for (; ; ) {
if (run.get())
break;
}
while (run.get()) {
addElement(mapOfSets, "Resource", UUID.randomUUID().toString());
addElement(mapOfSets, "Resource", UUID.randomUUID().toString());
addElement(mapOfSets, "Order", UUID.randomUUID().toString());
addElement(mapOfSets, "Order", UUID.randomUUID().toString());
addElement(mapOfSets, "Order", UUID.randomUUID().toString());
addElement(mapOfSets, "Activity", UUID.randomUUID().toString());
addElement(mapOfSets, "Activity", UUID.randomUUID().toString());
mapOfSets.removeElement("Resource", "Ball");
mapOfSets.removeElement("Order", "ToStock");
mapOfSets.removeElement("Activity", "ToStock");
}
return true;
}
private void addElement(MapOfSets<String, String> mapOfSets, String type, String id) {
logger.info("Adding " + type + " " + id);
mapOfSets.addElement(type, id);
}
private MapOfSets<String, String> buildMapOfSets() {
MapOfSets<String, String> mapOfSets = synchronizedMapOfSets(new MapOfSets<>(true));
mapOfSets.addElement("Resource", "Ball");
mapOfSets.addElement("Resource", "Car");
mapOfSets.addElement("Order", "StockOrder");
mapOfSets.addElement("Order", "ToStock");
mapOfSets.addElement("Order", "FromStock");
mapOfSets.addElement("Activity", "FromStock");
mapOfSets.addElement("Activity", "ToStock");
assertEquals(new HashSet<>(Arrays.asList("Ball", "Car")), mapOfSets.getSet("Resource"));
assertNull(mapOfSets.getSet("xxx"));
return mapOfSets;
}
}