[SOLVED] When String is the object of the lock, which is better String.intern() or Striped?

Issue

Imagine a scenario where we need to lock based on the device id, which is a string. Many people recommend using String.intern() as the lock object, but some people recommend using Striped for concurrency control, such as the following code:

import com.google.common.util.concurrent.Striped;

import java.util.concurrent.locks.Lock;

public class Test {

    private Striped<Lock> striped = Striped.lock(8);

    public void businessLogicByStringIntern(String deviceId) {
        // use deviceId.intern() as lock
        synchronized (deviceId.intern()) {
            // execute code thread-safely
        }
    }

    public void businessLogicByStriped(String deviceId) {
        // use striped lock
        synchronized (striped.get(deviceId)) {
            // execute code thread-safely
        }
    }

}

Which implementation is more recommended, businessLogicByStringIntern or businessLogicByStriped?

Reference:
Striped (Guava: Google Core Libraries for Java 19.0 API)

Solution

In the first version, you get a unique lock per device as distinguished by the deviceId string. The intern is necessary in this case.

In the second version, you have only (say) 8 locks and they are striped across all of the devices.


What is the difference?

  • The first version creates more primitive locks. But primitive locks are cheap unless there is contention on a lock. With this version you only get lock contention if two or more threads are really trying to do something with the same device.

  • The second version never creates more than 8 locks, and they are Lock objects. The Lock API provides more functionality than a primitive lock … if that is useful to you.

    But with this version you get more contention. For example, if two different devices use the same striped lock, you get mutual exclusion for those two devices … which you probably don’t want.

There is also a theoretical issue with using interned strings as lock identifiers. The space of interned strings is JVM wide, so if you have different parts of your application independently locking things this way (e.g. using the interned device id strings), they can potentially interfere. The two parts of the application might end up accidentally sharing locks. (This issue is discussed in more depth in https://stackoverflow.com/a/134154/5973816.)

There are ways to avoid this if it is a real issue. For example, the two parts of the application could add (different) prefixes to the strings before interning, etc.


Which is better?

Well it depends on 1) what you are optimizing for and 2) how long the locks are liable to be held; i.e. the cost of unwanted contention caused by striping.

Under most circumstances, the first version (a distinct lock per device) will be better. But, if you have a huge number of devices, the cost of a huge number of interned strings might be significant. In that case, AND if the locks are held for a very short time, then lock striping might be better.

Answered By – Stephen C

Answer Checked By – David Goodson (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *