Search code examples
javastringspring-bootcontrollersynchronized

String literals with same value doesn't keep a synchronized lock in controller


i know that String literals point to the same object and have same reference for same values. so it was just right for my case of synchronization. i tested the below code and it came just how i wanted. i wanted to have a lock on same Strings and don't have lock when Strings were different.

public class SampleThread extends Thread {
String lock;
public SampleThread(String s) {
    this.lock = s;
}
@Override
public void run() {

    long id = this.getId();
    synchronized (lock) {
        for (int i = 0; i < 1000; i++) {
            System.out.println("thread with id: "+id);
        }
    }
}

public static void main(String[] args) {

    SampleThread s1 = new SampleThread("mina");
    SampleThread s2 = new SampleThread("mina");

    s1.start();
    s2.start();
}
}

first thread completed and then the second thread started. i put the same code in my controller and this doesn't work for the same literals.two requests enter the block without considering the string lock. is there any way to fix this? this is the sample i tested and doesn't work.

@RequestMapping("/test/{name}")
public void test(@PathVariable("name") String test) throws InterruptedException {

    String a = test;
    synchronized (String.valueOf(a)) {
        System.out.println("first");
        TimeUnit.SECONDS.sleep(4);
        System.out.println("finish");
    }

}

Solution

  • The compiler will optimize strings of the same value and make them exactly the same instance when they are part of the code, as in:

    if ("abc" == "abc")
    

    But if the string is created at runtime like in your case, where the String test is parsed from the URL then it is not optimized in the same way and it is its own instance.

    So http://localhost/test/name1 executed twice would create two separate string instances that would not compare == and that means that synchronizing on it would not give the results you expect.

    It seems like you are trying to make processing of the same "test" values to be synchronous but allow different test values to process asynchronously. If this is the case then you can do something like keep a map of values that are being processed where the "test" value is the key and an instance of Object is stored as the value to used as the mutex. Then synchronize on the mutex after looking it up.