Why do I get intermittent ConcurrentModificationExceptions in the constructor of LinkedHashMap? - java

A version of the static method append() below, is sometimes called simultaneously by various threads:
public class CMEDemo {
private static final Logger LOG = Logger.getLogger(CMEDemo.class.getName());
private static final String[] SOME_DEMO = {"albino", "mosquito", "libido"};
private static final Set<String> SET_SOURCE = new LinkedHashSet<>(Arrays.asList(SOME_DEMO));
public static void append() {
//ConcurrentModificationException is thrown in the constructor for modifiableCopy.
LinkedHashSet<String> modifiableCopy = new LinkedHashSet<>(getBuiltInFunctions());
//Exception is not thown here, or later.
Set<String> doomed = modifiableCopy.stream()
.filter(f -> f.contains("quit")).collect(Collectors.toSet());
for (String found : doomed) {
LOG.log(Level.INFO, found);
}
}
public static Set<String> getBuiltInFunctions() {
return Collections.unmodifiableSet(SET_SOURCE);
}
}
Normally, all works as expected, but sometimes the LinkedHashSet constructor throws a ConcurrentModificationException:
java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:742)
at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042)
at java.util.AbstractCollection.addAll(AbstractCollection.java:343)
at java.util.LinkedHashSet.<init>(LinkedHashSet.java:169)
My JVM is Java 1.8_212. If I setup a test case to spawn multiple threads and let it run for a while, append() eventually throws ConcurrentModificationException. Why is this exception thrown, and how do I safely get a LinkedHashSet?

Other code was modifying the Set SET_SOURCE which was wrapped, not copied, by Collections.unmodifiableSet(..), and that caused intermittent exceptions during the construction of the copy in LinkedHashSet.
So I replaced getBuiltInFunctions() with the equivalent of
public static Set<String> getBuiltInFunctions() {
synchronized (SET_SOURCE) {
return Collections.unmodifiableSet(new HashSet<>(SET_SOURCE));
}
}
private static Set<String> getFunctionsRW() {
synchronized (SET_SOURCE ) {
return SET_SOURCE;
}
}

Related

Possibility of Race Condition on using Java Locks

I've written a Java class and someone has reviewed the code and insisted that there could be a race condition in method calculate. Here's a simplified version of the class code:
public class MyClass {
private List<Integer> list;
private final ReadWriteLock lock;
public MyClass() {
list = new ArrayList<>();
lock = new ReentrantReadWriteLock();
}
public void add(Integer integer) {
lock.writeLock().lock();
try {
list.add(integer);
} finally {
lock.writeLock().unlock();
}
}
public void deleteAll() {
lock.writeLock().lock();
try {
list.clear();
} finally {
lock.writeLock().unlock();
}
}
public Integer calculate() {
List<Integer> newList = new ArrayList<>();
Integer result = 0;
lock.readLock().lock();
try {
list.forEach(integer -> {
// calculation logic that reads values from 'list' and adds only a subset of elements from 'list' in 'newList'
});
} finally {
lock.readLock().unlock();
}
setList(newList);
return result;
}
private void setList(List<Integer> newList) {
lock.writeLock().lock();
try {
list = newList;
} finally {
lock.writeLock().unlock();
}
}
}
Now my question is:
Can a race condition really happen in this method, and if so how can I solve it (either using locks or using any other method to make the class thread safe)?
Any advice would be appreciated.
There is a time gap between creation of newList and call to setList(newList). We may assume this time gap is arbitrary long, and everything can happen when it lasts, e.g. another thread adds an object which must be retained, but it will be lost when call to setList(newList) removes list with that new object.
In fact, the method calculate is modifying and should do all the work under write lock.
To clarify the above ... the statement
List<Integer> newList = new ArrayList<>();
... instantiates a data-structure (list ...) that will subsequently be used within the block of code that is intended to be protected by lock.readLock().lock();, but is not contained within it. Therefore it is not protected.
To remedy the problem, the declaration of newList should not include initialization. Nothing which affects the presumed value of this variable should exist outside of the lock-protected block.

Java Set gets full

I am making a particle emitter.
Every "Rendered" object is stored in a HashSet, and when there's lots of particles on the screen, the console spits out concurrent modification exceptions. I usually have a short lifetime on these particles so they get deleted after several seconds, but I am sure this could potentially be a problem in the future. How can I fix this?
EDIT: Code:
public class UpdatedManager {
private static Set<Updated> updates = new HashSet<>();
private UpdatedManager() {}
public static Set<Updated> getUpdates() {
return new HashSet<Updated>(updates);
}
public static boolean registerUpdated(Updated u) {
return updates.add(u);
}
public static boolean unregisterUpdated(Updated u) {
return updates.remove(u);
}
public static void update() {
for (Updated up : new HashSet<Updated>(updates))
up.update();
}
public static Set<GameObject> getGameObjects() {
Set<GameObject> objs = new HashSet<>();
for (Updated up : new HashSet<Updated>(updates)) {
if (up instanceof GameObject)
objs.add((GameObject) up);
}
return objs;
}
public static Set<GameObject> getGameObjectsByName(String name) {
Set<GameObject> objs = new HashSet<>();
for (GameObject go : new HashSet<GameObject>(getGameObjects())) {
if (go.getName() != null && go.getName().equals(name))
objs.add(go);
}
return objs;
}
public static Set<Particle> getParticles() {
Set<Particle> parts = new HashSet<>();
for (Updated up : new HashSet<Updated>(updates)) {
if (up instanceof Particle)
parts.add((Particle) up);
}
return parts;
}
}
A ConcurrentModificationException means you modified the set while iterating over it. It does not mean the set is full.
For example, the following code will throw a ConcurrentModificationException:
Set<String> set = new HashSet<>();
set.add("Hello");
for(String s : set)
set.add(s+" world");
Note that you are not guaranteed to get a ConcurrentModificationException, so you should avoid catching it. You should instead fix your code so that it doesn't cause the problem.
What makes you think that the set is full?
Concurrent modification exceptions mean that the set is being accessed by different threads in an unsafe manner.
Try a synchronised set using the Collections utilities
HashSet hashSet = new HashSet();
Set set = Collections.synchronizedSet(hashSet);
or use the synchronized keyword for the method accessing the set.

Spurious ConcurrentModificationException

I'm having some issues with a piece of java code which keeps triggering a ConcurrentModificationException. I can't really figure out what is going on, this is a fairly simple static class, not sure why it would be throwing this exception as everything is synchronized. This piece of code has been heavily used for several years, so it's odd that it would start having issues at this point:
java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
at java.util.LinkedList$ListItr.next(LinkedList.java:886)
at DataSubscriptionManager.sendMessages(DataSubscriptionManager.java:18)
private static HashMap<DataClass,LinkedList<DataSubscriber>> subscriberMap = new HashMap();
public static void sendMessages(LinkedList messages, DataClass dataClass) {
synchronized (subscriberMap) {
LinkedList<DataSubscriber> subscribers = subscriberMap.get(dataClass);
if (subscribers != null) {
for (DataSubscriber sub: subscribers) { *** EXCEPTION HAPPENS HERE***
if (sub != null) {
sub.sendMessages(messages);
}
}
}
}
}
public static void addDataSubscriber(DataSubscriber sub, DataClass dataClass) {
synchronized (subscriberMap) {
LinkedList<DataSubscriber> subscribers = subscriberMap.get(dataClass);
if (subscribers == null) {
subscribers = new LinkedList();
subscriberMap.put(dataClass,subscribers);
}
while (subscribers.remove(sub)) {}
subscribers.add(sub);
}
}
public static void removeDataSubscriber(DataSubscriber sub, DataClass dataClass) {
synchronized (subscriberMap) {
LinkedList<DataSubscriber> subscribers = subscriberMap.get(dataClass);
subscribers.remove(sub);
}
}
What's happening is that your collection is being modified while you are iterating over it.
It's could be another thread, or it's possible one of your subscribers is either unsubscribing, or subscribing to a different dataClass in response to the message it receives.
You can try using Collections.synchronizedList(subscribers) which may help avoiding this problem.

Java ConcurrentModificationException when using list.remove()

I've got a method called removeSup which is supposed to remove an object Supplement from a list of supplements.
this is the code for the method:
private static void removeSup(Supplement supToRemove, List<Supplement> listToRemoveFrom) {
Iterator<Supplement> iterator = listToRemoveFrom.iterator();
while(iterator.hasNext()){
if(iterator.next().equals(supToRemove)){
iterator.remove();
}
}
}
there is a class called magazine which defines the list of supplements.
public class Magazine {
private List<Supplement> supList;
public List<Supplement> getSupList() {
return this.supList;
}
public void setSupList(List<Supplement> supList) {
this.supList = supList;
}
public Magazine(Double cost, String _name){
this.supList = new ArrayList<>();
this.weekCost = cost;
this.name = _name;
}
}
the class supplement has the following constructor
public Supplement(String _name, Double _price, String _magName ){
this.name=_name;
this.price=_price;
this.magName = _magName;
}
in the main class client there is a search that the user can do to remove a certain Supplement
private static void searchSup(){
System.out.println("Search for Supplement");
String search = scanner.nextLine();
for (Supplement sup : magazine.getSupList()) {
if (!sup.getSupName().equalsIgnoreCase(search)) {
//do something
}
else{
removeSup(sup,magazine.getSupList());
}
}
}
the main method in the client class is as follows:
private Magazine magazine;
public static void main(String[] args) {
magazine = new Magazine(3.0, "pop");
List<Supplement> startList = new ArrayList<>();
startList.add(new Supplement("Nat Geo", 3.0,"pop"));
startList.add(new Supplement("Discovery", 5.0,"pop"));
startList.add(new Supplement("Health", 6.3,"pop"));
startList.add(new Supplement("IT", 8.3,"pop"));
magazine.setSupList(startList);
searchSup();
}
When I run this program and type any of the added supplements, i get an error
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at Client.searchSup(Client.java:131)
at Client.searchSup(Client.java:140)
at Client.main(Client.java:588)
is it the for loop i am using to search giving me an error? if so how would i go about fixing this?
You generally shouldn't modify a Collection while iterating over it. It's fine to modify elements, but you really shouldn't remove something from a Collection while iterating. See here: http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html. Also, the Javadoc for ConcurrentModificationException may be helpful.
You might try returning a new list with the Supplement removed:
private static List<Supplement> removeSup(Supplement supToRemove, List<Supplement> listToRemoveFrom) {
List<Supplement> filteredSupplements = new ArrayList<Supplement>();
for(Supplement supplement : listToRemoveFrom) {
if(!suppplement.equals(supToRemove)){
filteredSupplements.add(supplement);
}
}
return filteredSupplements;
}
It seams that the "magazine" is local var in the method of main, not accessible to searchSup.Fix it like
private void searchSup(Magazine magazine)
{
//...
}
and more details if you can provide, the codes in Line 131 and 140 will be helpful.
I figured out that the search i was doing was not working with what i wanted to do so i created a method which returns an integer of the Supplement in the list.
private static int indexOfSup(List<Supplement> supSearchList, String nameOfSup) {
for (Supplement sup : supSearchList) {
if (sup.getSupName().equalsIgnoreCase(nameOfSup)) {
return supSearchList.indexOf(sup);
}
}
return -1;
}
i then use this integer to remove from the list.
a simple List.Remove(index) worked fine
Thanks for all the replies.

Java ExecutorService Task Spawning

I have an ExecutorService that is used to handle a stream of tasks. The tasks are represented by my DaemonTask class, and each task builds a response object which is passed to a response call (outside the scope of this question). I am using a switch statement to spawn the appropriate task based on a task id int. It looks something like;
//in my api listening thread
executorService.submit(DaemonTask.buildTask(int taskID));
//daemon task class
public abstract class DaemonTask implements Runnable {
public static DaemonTask buildTask(int taskID) {
switch(taskID) {
case TASK_A_ID: return new WiggleTask();
case TASK_B_ID: return new WobbleTask();
// ...very long list ...
case TASK_ZZZ_ID: return new WaggleTask();
}
}
public void run() {
respond(execute());
}
public abstract Response execute();
}
All of my task classes (such as WiggleTask() ) extend DaemonTask and provide an implementation for the execute() method.
My question is simply; is this pattern reasonable? Something feels wrong when I look at my huge switch case with all its return statements. I have tried to come up with a more elegant lookup table solution using reflection in some way but can't seem to figure out an approach that would work.
Do you really need so many classes? You could have one method per taskId.
final ResponseHandler handler = ... // has many methods.
// use a map or array or enum to translate transIds into method names.
final Method method = handler.getClass().getMethod(taskArray[taskID]);
executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
method.invoke(handler);
}
});
If you have to have many classes, you can do
// use a map or array or enum to translate transIds into methods.
final Runnable runs = Class.forName(taskClassArray[taskID]).newInstance();
executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
runs.run();
}
});
You can use an enum:
public enum TaskBuilder
{
// Task definitions
TASK_A_ID(1){
#Override
public DaemonTask newTask()
{
return new WiggleTask();
}
},
// etc
// Build lookup map
private static final Map<Integer, TaskBuilder> LOOKUP_MAP
= new HashMap<Integer, TaskBuilder>();
static {
for (final TaskBuilder builder: values())
LOOKUP_MAP.put(builder.taskID, builder);
}
private final int taskID;
public abstract DaemonTask newTask();
TaskBuilder(final int taskID)
{
this.taskID = taskID;
}
// Note: null needs to be handled somewhat
public static TaskBuilder fromTaskID(final int taskID)
{
return LOOKUP_MAP.get(taskID);
}
}
With such an enum, you can then do:
TaskBuilder.fromTaskID(taskID).newTask();
Another possibility is to use a constructor field instead of a method, that is, you use reflection. It is much easier to write and it works OK, but exception handling then becomes nothing short of a nightmare:
private enum TaskBuilder
{
TASK_ID_A(1, WiggleTask.class),
// others
// Build lookup map
private static final Map<Integer, TaskBuilder> LOOKUP_MAP
= new HashMap<Integer, TaskBuilder>();
static {
for (final TaskBuilder builder: values())
LOOKUP_MAP.put(builder.taskID, builder);
}
private final int index;
private final Constructor<? extends DaemonTask> constructor;
TaskBuilder(final int index, final Class<? extends DaemonTask> c)
{
this.index = index;
// This can fail...
try {
constructor = c.getConstructor();
} catch (NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
// Ewww, three exceptions :(
public DaemonTask newTask()
throws IllegalAccessException, InvocationTargetException,
InstantiationException
{
return constructor.newInstance();
}
// Note: null needs to be handled somewhat
public static TaskBuilder fromTaskID(final int taskID)
{
return LOOKUP_MAP.get(taskID);
}
}
This enum can be used the same way as the other one.

Categories

Resources