2

Intern method will keep adding the strings into the pool which is a bad idea for long term process.Exact scenario is like this :

// rowKey variable defined below is the value of a row key extracted from a 
// table of the db. There can be many rowKey values. (>100000).
String rowKey = "rowId_123123"; 
/* Once rowKey is extracted, then if another thread extracts the same 
   row from the table, then it should be put to wait. Object locking will
   put all the threads to wait irrespective of the rowKey value which will 
   hinder performance. Interning the string will put it into the pool till 
   jvm execution.
*/ 

Is there a way to achieve this without interning the string and synchronizing only on the string. Or if there is a way to convert a string into an object which can be visible to all the threads?

Vikas Bansal
  • 147
  • 6
  • 3
    I'm not sure what you are trying to achieve here, since synchronizing and interning are two entirely different concepts. Can you explain what problem you are trying to solve? – Keppil Apr 05 '15 at 12:14
  • Strings are immutable not variable. Exactly how would you like to synchronize on one? – candied_orange Apr 05 '15 at 12:14
  • How do you expect us to guess what you are talking about? – Sergey Kalinichenko Apr 05 '15 at 12:15
  • Your question is very unclear. Please flesh it out. – Hovercraft Full Of Eels Apr 05 '15 at 12:15
  • Strings _should not_ be used as monitors/locks specifically _because_ they are pooled/interned. – Mick Mnemonic Apr 05 '15 at 12:18
  • 2
    @Keppil . I need to surround a block of code with `synchronize` construct. I want to do it with a string literal. But to achieve this i have to use intern method so that the string literal becomes available to all the thread and another thread is blocked on the same string literal. But once a string is interned it stays in memory till jvm keeps executing. My exact problem is to apply a soft lock on the row keys of database. – Vikas Bansal Apr 05 '15 at 12:21
  • Don't use a String, use `private final Object lock` instead. – Mick Mnemonic Apr 05 '15 at 12:27
  • Mick, why exactly can I not use the monitor of an interned String? I don't get your point. Also, only String literals are interned unless `String.intern()` is explicitly called. Vikas, sound like you need your own String cache. Building a proper object cache is a hard thing to do and requires that you know a bit about different reference types in Java. If you google it, you'll discover that there are good object caches out there that you may use. I know Guava has an awesome cache builder. – Martin Andersson Apr 05 '15 at 12:52
  • @Martin, synchronizing on Strings is generally considered a bad practice (see e.g. [this answer](http://stackoverflow.com/a/3985030/905488)). Doing that (or rolling out something based on that approach) will make your code non-idiomatic and difficult to understand/maintain. Instead of synchronizing on Strings, the OP should consider using a concurrency library that supports locks that can be looked up by an id/name -- maybe there's something in e.g. `java.util.concurrent` that could be used for this purpose. – Mick Mnemonic Apr 05 '15 at 13:59
  • Yeah good point =) Thanx. As an aside, I prefer synchronized if all my threads are writer threads and I know contention will be high (i.e. busy waiting will not be beneficial). In the majority of all cases, I pick `ReentrantReadWriteLock`. – Martin Andersson Apr 05 '15 at 14:14
  • @VikasBansal Please mark an answer as accepted for future visitors to this question (not necessarily my answer of course). It will help other future question readers if they know the answer that helped you the most. – Emily Mabrey Jan 11 '16 at 14:33

2 Answers2

3

Use Guava Striped Class

In the past I have had success using the Guava Striped class for this type of Object to Lock mapping operation. For in-depth details on the Striped class please see the Guava wiki or the Striped JavaDoc. Essentially, using a Striped<Lock> instance to index Lock values via String keys is a thread-safe way to generate Lock instances that minimizes memory footprint while maximizing concurrency. If you are avoiding 3rd party libraries you could instead implement your own Map<Object, Lock> wrapper (perhaps mixing in WeakReference) to roll your own less full-featured version of the Striped class (be sure to directly compare hashcodes instead of relying on the == operator or the equals methods!). In either case, the Striped class is a good place to start learning about how to implement your lock generation/retrieval.


Example

Note that for a specific use case, like for achieving atomic operations for database rows, you might want to use a ReadWriteLock instead of a simple/basic Lock (using Semaphore is also possible). Also, please note that we don't need to intern the String object given to Striped.get() because the Striped class compares Object's for equality using hashcodes and the String class makes special guarantees about hashcodes between character equivalent Strings always being equal. This example does use an interned String (literal Strings are automatically interned) but the Striped class works perfectly well with a dynamically generated String which is not interned.

final Striped<Lock> stripedLocks = Striped.lock(10);

final String rowID = "rowLock123";

final Lock rowLock = stripedLocks.get(rowID);

try{
     rowLock.lock();//Could also use tryLock or lockInterruptibly methods

     //... we are successfully in the fenced/locked code block now
     //... put your concurrency sensitive code here

}finally{
     rowLock.unlock();
}

Correction

Don't use synchronized on the returned Lock object obtained from Striped.get(String)!

I am adding this explicit warning to not use the returned Lock object as a synchronized block's monitor because someone edited my answer to include an example that incorrectly used the returned Lock as a monitor object. For reference, this is what that would look like in the above example:

//DO NOT USE THE RETURNED LOCK LIKE THIS

final Lock rowLock = stripedLocks.get(rowID);

synchronized(rowLock){
    //...oh no
}

You should not use synchronized on a Lock because that defeats the entire purpose of using Lock! The Lock class is intended as a replacement for the use of synchronized blocks. Using them together sacrifices the benefits of Lock while still having the headaches and gotchas of synchronized.

Emily Mabrey
  • 1,528
  • 1
  • 12
  • 29
1

You can create your own object interning pool with weak referencing, so that it will expire unused keys.

Something like this should do it.. I think..:

final class RowLock {
    public final int rowId;

    private RowLock(int rowId) {
        this.rowId = rowId;
    }

    public static synchronized RowLock getLockObject(int rowId) {
        RowLock r = new RowLock(rowId);
        WeakReference<RowLock> rInternedRef = pool.get(r);
        if (rInternedRef != null) {
            RowLock rInterned = rInternedRef.get();
            if (rInterned != null) {
                return rInterned;
            }
        }
        pool.put(r, new WeakReference<>(r));
        return r;
    }

    private static final WeakHashMap<RowLock,WeakReference<RowLock>> pool =
        new WeakHashMap<>();

    @Override
    public int hashCode() {
        return rowId;
    }

    @Override
    public boolean equals(Object obj) {
        return obj == this || (obj instanceof RowLock && ((RowLock)obj).rowId == rowId);
    }
}

If your row IDs are not numeric, you can use a String rowId instead and it will work just as well.

When you want to lock on a row, call getLockObject and then synchronize on the returned object, which will have been "interned" via the map, so that all threads will get the same object. Once nothing is strongly referring to a RowLock instance any more, it will be eligible for garbage collection, because its interning pool only refers to it with weak references.

Boann
  • 48,794
  • 16
  • 117
  • 146
  • Please note I have already spent a lot of time editing my answer, so I may not have thought this all the way through. That said however, I think using the `==` operator means that using `String` instances for `rowId` will require you to intern them to avoid a specific `String` instance going out of scope and irretrievably losing access to/leaking a `Lock`. I would recommend using `Integer` (or maybe generics) instead of `int` and adding a check for hashcode equality between instances of `rowId` (you can potentially leave your current operations as an efficiency boost). – Emily Mabrey Apr 29 '18 at 21:09
  • @Boann Why did you make getLockObject synchronized because it will return same object reference every time for the same rowId? Is it to prevent race condition? – Dhruv Kapatel Feb 01 '19 at 15:32
  • @DhruvKapatel Yes. The method has to be synchronized to stop two (or more) threads entering the method at the same time and returning different lock objects for the same ID. – Boann Feb 01 '19 at 15:42
  • @Boann Does this solution work if we have multiple tomcats instances and load balancer (distributed environment)? – Dhruv Kapatel Mar 04 '19 at 14:42
  • @DhruvKapatel I don't know if I know what you mean. If you're running multiple Java instances, then you have multiple instances of the `WeakHashMap`. It's not magic. It just answers the question of how to `synchronize` on an identifier without using the String intern pool. – Boann Mar 04 '19 at 22:42