Search code examples
javamultithreadingtreemap

java: issue while access value from TreeMap in multithreading


I have use TreeMap to store key value. For key using custom object. But once I have faced very strange issue, I am not able to get value which I have set earlier(with same key). below is my code

public final class TestOptions implements Cloneable {   
    private Map<StorageSystemOptionKey, Object> options = new TreeMap<StorageSystemOptionKey, Object>();
private static final class StorageSystemOptionKey implements Comparable<StorageSystemOptionKey> {
    /** Constant used to create hashcode */
    private static final int HASH = 31;

    private final Class<? extends StorageRepository> storageRepositoryClass;

    /** The option name */
    private final String name;

    private StorageSystemOptionKey(Class<? extends StorageRepository> storageRepositoryClass, String name) {
        this.storageRepositoryClass = storageRepositoryClass;
        this.name = name;
    }

    public int compareTo(StorageSystemOptionKey o) {
        int ret = storageRepositoryClass.getName().compareTo(o.storageRepositoryClass.getName());
        if (ret != 0) {
            return ret;
        }
        return name.compareTo(o.name);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        final StorageSystemOptionKey that = (StorageSystemOptionKey) o;

        if (!storageRepositoryClass.equals(that.storageRepositoryClass)) {
            return false;
        }
        if (!name.equals(that.name)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        result = storageRepositoryClass.hashCode();
        result = HASH * result + name.hashCode();
        return result;
    }

    }

    void setOption(Class<? extends StorageRepository> fileSystemClass, String name, Object value) {
        options.put(new StorageSystemOptionKey(fileSystemClass, name), value);
    }

    Object getOption(Class<? extends StorageRepository> fileSystemClass, String name) {
        StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
        return options.get(key);
    }

    boolean hasOption(Class<? extends StorageRepository> fileSystemClass, String name) {
        StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
        return options.containsKey(key);
    }

    public int compareTo(TestOptions other) {
        if (this == other) {
            return 0;
        }

        int propsSz = options == null ? 0 : options.size();
        int propsFkSz = other.options == null ? 0 : other.options.size();
        if (propsSz < propsFkSz) {
            return -1;
        }
        if (propsSz > propsFkSz) {
            return 1;
        }
        if (propsSz == 0) {
            return 0;
        }

        int hash = options.hashCode();
        int hashFk = other.options.hashCode();
        if (hash < hashFk) {
            return -1;
        }
        if (hash > hashFk) {
            return 1;
        }
        return 0;
    }

    @Override
    public Object clone() {
        TestOptions clone = new TestOptions();
        clone.options = new TreeMap<StorageSystemOptionKey, Object>(options);
        return clone;
    }
}

calling method to set and get like

public abstract Class<? extends StorageRepository> getStorageRepositoryClass();


public Class<? extends StorageRepository> getStorageRepositoryClass() {
        return MyImpl.class;
}
TestOptions opt =new TestOptions(); // shared accross all Threads
Object getProperty(String name) {
    return opt.getOption(getStorageRepositoryClass(),  name);       
}
 void setProperty(String name, Object value) {
        opt.setOption(getStorageRepositoryClass(), name, value);
    }

Using set and get method in multi-threaded application. queries:
I am calling set/get in multiple time then also I was not able to get value which was set earlier(same key)
Is this due to Treeset implementation is not synchronized or problem with hashCode, equals or compareTo method implementation?


Solution

  • On a quick glance your compareTo(), equals() and hashCode() look fine. Note that TreeMap will mostly use compareTo() to find elements so that method needs to be correct (your's looks technically correct).

    However, TreeMap and TreeSet (as well as other basic collections and maps) are not thread-safe and thus concurrent modifications can cause all kinds of unexpected behavior. We once had a case where 2 threads were trying to add a single element to a hashmap and the threads ended up in an endless loop because the internal list to resolve clashes produced a cycle (due to the concurrent put).

    So either use the ConcurrentXxxx maps and collections or synchronize access to yours.