1

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)

Poison
  • 389
  • 2
  • 14

1 Answers1

2

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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Good answer, but some people say that because `String` objects are shared by JVM, there is a potential risk of sharing a lock with other unknown places, or causing system deadlock. See: https://stackoverflow.com/a/134154/5973816 – Poison Mar 02 '22 at 13:30
  • If you use have intersecting *id spaces* for devices and something else ... and you use the same approach for locking in both id spaces ... yes, you might get lock interference. But you get that problem with lock striping too. And, the deadlock problem is orthogonal. That deadlocks are cause by threads acquiring multiple locks, and acquiring them in different orders. – Stephen C Mar 02 '22 at 15:30
  • 1
    Anyway ... solutions to the (theoretical) lock interference problem with locking interned strings include 1) put a unique / distinguishing prefix on the front of the device id string, or 2) use UUIDs as device ids. – Stephen C Mar 02 '22 at 23:48
  • I think their difference is that `String` objects are JVM-wide visible, while `Striped` is a local variable. If we write a web application and deploy it to tomcat, there is a potential conflict problem in using `String` object as lock, because we cannot guarantee that other web applications in tomcat do not use the same `String` object as lock, but local variables such as Striped, others are impossible to use. – Poison Mar 03 '22 at 01:54