7

I need your advice. For a start I would like to describe preconditions.

  1. I have some third party Java objects with default java.lang.Object's hashCode() and equals() implementation. Comparable interface is not implemented. The size is insignificant.
  2. I need to store such objects for some time in memory. I will read and write them from different threads in 50/50 ratio (approximately 50% reads and 50% writes).
  3. The order of the objects is not important. I just want to have possibility to take some object from the store, that's all. With take I mean both get and remove at the same time.
  4. Sure, I want it to work as fast as possible with lowest memory consumption. I'm trying to avoid any synchronizations in my code.

First I've tried to solve this problem by myself. I've immediately rejected CopyOnWriteArray* collections due to high memory consumption. I've read it's better to use them in case of rare writes. ConcurrentHashMap in general suites for my needs but I didn't find way to make take operation atomic without synchronization. I've stopped with my investigation on ConcurrentSkipListSet collection. It contains pollFirst method which is pretty good suite to take objects.

I've implemented my solution with ConcurrentSkipListSet as a basis. I've got everything works fine except one small detail. As I mentioned above objects I'm working with don't implement Comparable. So to use chosen collection I have to somehow implement Comparator. Here's my implementation of this interface. In this example I've used directly java.lang.Object instead of my objects type. I've done this 'cause implementation is completely the same, difference is only in generic part of the class.

import java.util.Comparator;

public class ObjectComparator implements Comparator<Object> {

    public int compare(Object o1, Object o2) {
        return o1.hashCode() - o2.hashCode();
    }
}

Cons of this implementation is obvious. I've found there's no guarantee that two different objects will have different hash codes. In such case it's possible to loose some objects which is not acceptable. I've thought to return some random number in case of equal hash codes for different objects but I'm not sure it will not break ConcurrentSkipListSet implementation.

Regarding situation described I have two general questions.

  1. Is it possible to implement Comparator for my object in as such way to don't return 0 for different objects and keep ConcurrentSkipListSet operability?
  2. Is there some other way to store my objects?

Thank you in advance for your answers.

Community
  • 1
  • 1
artspb
  • 1,127
  • 1
  • 10
  • 19
  • How big is your objects? CopyOnWrite strategy is the best if you have < 100 objects, due to semaphore heaviness. And if your collections is not too big you can fit everything to Eden generation with the very efficient algorithm. – Taky Nov 25 '14 at 09:37
  • @Taky The size of the collection is not specified. It could be 100 or 10k. I don't know. I'm trying to find some general way to solve this problem. Also I didn't catch the second part of your comment. What do you mean with "Eden generation"? – artspb Nov 25 '14 at 10:35
  • By "Eden generation" I mean young JVM object's pool. – Taky Nov 25 '14 at 12:37

3 Answers3

3

Probably you are looking for ConcurrentLinkedQueue, this will store the items based on the FiFo (first in first out) order. Because of this no hashcode and comparable requirements are present. The implementation of this queue is very efficient, without any internal locking.

One difficulty that you have (when not using locks) is that you can not check if the collection is not empty and then take one (because it might have changed after your check and before your action). Therefor the take function will return null when nothing is present. If you don't want to keep polling for data, then you can also resort to classes implementing the BlockingQueue interface, this offers functions that wait until data is available.

Thirler
  • 20,239
  • 14
  • 63
  • 92
1

1: You could implement your comparator as such:

public int compare(Object o1, Object o2) {
    if (o1 == o2) {
        return 0;
    } else {
        int dif = o1.hashCode() - o2.hashCode();
        if (dif != 0) {
            return dif;
        } else {
            return 1; //Might cause issues
        }
    }
}

2: You can make any normal collection synchronized using java.util.Collections.synchronizedCollection() (or any other synchronized- method). For example, if you synchronize a LinkedList, you can use remove(index) to take an object.

David ten Hove
  • 2,748
  • 18
  • 33
  • 1. I've mentioned such implementation in my question. Are you sure it'll not break `ConcurrentSkipListSet` logic? 2. Yes, it's good option. But in case of high load synchronization will lead to performance problems. I'm trying to avoid it. – artspb Nov 25 '14 at 10:31
  • @Art Hm I misread about the comparator. I suppose you could also put every object in a Comparable wrapper with an index based on the order in which they were created. Would that work? – David ten Hove Nov 25 '14 at 10:35
  • 1
    In general it should work. But implementation will be quite complex. For instance I have to use `WeakReference` in such case to do not depend on the object itself. As a technical solution of this problem it works, but from my point of view it looks quite ugly. Anyway thank you fro the idea! – artspb Nov 25 '14 at 10:46
  • The "might" in "might cause trouble" is wrong. This will cause trouble because skiplists do need a total order of its elements and this clearly violates it (it's also broken in 50% of all cases even if the hashcode was unique - subtraction for comparisons does not work) – Voo Nov 26 '14 at 06:49
1

ConcurrentHashMap in general suites for my needs but I didn't find way to make take operation atomic

Why not use remove in ConcurrentHashMap? Seems it is what you need.

Denis Borovikov
  • 737
  • 5
  • 9
  • But to use `remove` method I have to somehow obtain object first. I can do it with `iterator` trying to remove each object until I'll succeed. If there're lot of threads it'll take lot of time trying to remove already deleted entries. It's because via `iterator` of `ConcurrentHashMap` you're working with some state of the map, but not with the real data. From opposite site `pollFirst()` method of `ConcurrentSkipListSet` operating with the real data. In such case it should be much faster. – artspb Nov 25 '14 at 16:25