J L
J L

Reputation: 987

Generics with multi threads and instance sharing

I have a generic class that can be sketched like this

public class Shared<T extends EntityBase> {

    private HashMap<String, Class<? extends Executor<T>>> classes;
    private HashMap<String, ? super Executor<T>> instances;
    private List<Entity> entities;
    private Compiler compiler;

   // Constructor and getters

    public void put(Entity entity, Source source) {
        Class<?> cls = compiler.load(source);
        put(entity, (Class<? extends Executor<T>>) cls );

    }

    private void put(Entity entity, Class<? extends Executor<T>> cls) throws IllegalAccessException, InstantiationException {
        classes.put(entity.getId(), cls);
        instances.put(entity.getId(), cls.newInstance());
        entities.add(entity);
    }
}

In my application this class is instanced once and several independent threads access it.

In particular a single thread is responsible of writing to it and several access its instances Map via a getter.

When I receive a Source instance and call the private put method, both maps and the list are updated.

In a debugging session with InteliiJ when the frame of the Singleton class is exited the this class losses its state and both Map and the List became empty once again.

How may I explain that? Why the Class<? extends Executor<T>> instances and the ? super Executor<T> instances are lost?

Upvotes: 0

Views: 29

Answers (1)

rzwitserloot
rzwitserloot

Reputation: 103244

generics are effectively just part of the compiler's imagination; at runtime that's all gone. Therefore, the generics in this example have absolutely nothing to do with what you're witnessing.

The code as pasted cannot produce the effect you see (where the map first changes, then reverts). But, more generally, if you are calling 'put' from different threads, this code is broken. There are no guards in place to prevent synchronous access. When you update a map simultaneously from different threads, the map spec specifically says anything can happen (In that sense, the map just resetting to empty is technically 'according to spec', in that anything is allowed because you're not doing the threading issue right).

The easy fix is to mark your second put method as 'synchronized'.

An alternative strategy is to use lists and maps from the java.util.concurrent package. However, no amount of using that will ever let you have classes, instances and entities be guaranteed in sync with each other. Only synchronized can do that.

Upvotes: 1

Related Questions