Synchronising access to a shared resource for asynchronous calls to a Spring Webserver does not seem to work with @Synchronized

Viewed 88

I have a SpringBoot web-server; on it, a route that manipulates the state of a singleton instance. This route is usually called by multiple actors, so synchronisation would be a must.

The corresponding method that is called by the route, is set():

@Synchronized
public fun <T> set(t: T) {
    val incomingState = State.from(t)
    if(incomingState == innerState){
        return
    }
    change(incomingState)
}

@Synchronized
private fun change(newState: State){
    this.stateAccessLock.lock()
    SystemLog.logResourceLocking("HomeState", isLocked = true)
    SystemLog.logHomeEvent("Changing HomeState from '${innerState.name}' to '${newState.name}'")
    try {
        Home.INSTANCE.EXTENSION.broadcastStateChange(
            innerState?.name,
            newState.name
        )
    }
    finally {
        innerState = newState
        Home.save()

        stateAccessLock.unlock()
        SystemLog.logResourceLocking("HomeState", isLocked = false)
    }
}

Before using the @Synchronized annotation, i have also tried to use a ReentrantLock, in the same class (As you can see, I've neglected removing it thus far). It is initialised as follows:

private var stateAccessLock = ReentrantLock()

@Autowired
fun HomeState(@Qualifier("stateAccessLock") stateAccessLock: ReentrantLock) {
    this.stateAccessLock = stateAccessLock
}

Now, the third and the last line in change() log the ressource beeing locked and given free. Filtering the logfile for these events gives me this:

[2020-10-09T16:07:01.709]       LOCK: Resource locked
[2020-10-09T16:07:01.710]       LOCK: Resource locked
[2020-10-09T16:07:01.725]       LOCK: Resource locked
[2020-10-09T16:07:01.727]       LOCK: Resource locked
[2020-10-09T16:07:01.818]       LOCK: Resource unlocked
[2020-10-09T16:07:01.828]       LOCK: Resource unlocked
[2020-10-09T16:07:01.829]       LOCK: Resource unlocked
[2020-10-09T16:07:01.839]       LOCK: Resource unlocked
[2020-10-09T16:11:33.499]       LOCK: Resource locked
[2020-10-09T16:11:33.523]       LOCK: Resource locked
[2020-10-09T16:11:33.525]       LOCK: Resource locked
[2020-10-09T16:11:33.692]       LOCK: Resource unlocked
[2020-10-09T16:11:33.692]       LOCK: Resource unlocked
[2020-10-09T16:11:33.693]       LOCK: Resource unlocked
[2020-10-09T16:11:35.881]       LOCK: Resource locked
[2020-10-09T16:11:35.959]       LOCK: Resource unlocked

Obviously, i would want something like this:

[2020-10-09T16:07:01.709]       LOCK: Resource locked
[2020-10-09T16:07:01.710]       LOCK: Resource unlocked
[2020-10-09T16:07:01.725]       LOCK: Resource locked
[2020-10-09T16:07:01.727]       LOCK: Resource unlocked
[2020-10-09T16:07:01.818]       LOCK: Resource locked
[2020-10-09T16:07:01.828]       LOCK: Resource unlocked
[2020-10-09T16:07:01.829]       LOCK: Resource locked
[2020-10-09T16:07:01.839]       LOCK: Resource unlocked

What am i missing out on? Thanks in advance!

1 Answers

Since you don't return anything from these methods I'd go for thread confinement instead and ditch locking altogether. This will also ensure that you won't get into a deadlock:

private val executor = Executors.newSingleThreadExecutor()

fun <T> set(t: T) {
    executor.submit {
        val incomingState = State.from(t)
        if (incomingState == innerState) {
            return
        }
        change(incomingState)
    }
}

Since you have a single-threaded executor you can be sure that the requests are processed in a serial manner (and without locking).

Related