this is just for discussion.
given solution: https://github.com/epibook/epibook.github.io/blob/master/solutions/java/concurrency/S2.java
the given solution locks to make sure that String wLast and String[] closestToLastWord is always read/written at the same time.
and since that’s all we need to guarantee for the given question, we can write a very simple solution without any locks.
if you write a wrapper class for both variables, e.g.
private static class Response {
public final String query;
public final String[] suggestions;
// and a constructor...
}
and because reads and writes are atomic for reference variables in Java per doc: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html
Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
also, if we want additional guarantee that the latest update to s_lastResponse is visible to other threads, we’ll use volatile keyword
any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads.
so then… this should suffice w/o locks.
class S3 extends SpellCheckService {
private static class Response {
public final String query;
public final String[] suggestions;
public Response(String query, String[] suggestions) {
this.query = query;
this.suggestions = suggestions;
}
}
// adhoc weird naming just to distinguish it from local vars
// note use of volatile to guarantee happens-before relationship.
private static volatile Response s_lastResponse = null;
public static void service(ServiceRequest req, ServiceResponse resp) {
String query = req.extractWordToCheckFromRequest();
// read of reference objects are atomic per java doc: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html
// (makes sense since it's just a pointer read)
// even if s_lastResponse gets overwritten by another thread immediately afterwards, we still have the old variable.
Response r = s_lastResponse;
if (query.equals(r.query)) {
resp.encodeIntoResponse(r.suggestions);
} else {
// otherwise, create a new Response instance
r = new Response(query, Spell.closestInDictionary(query));
resp.encodeIntoResponse(r.suggestions);
// then we'll save it.
// this operation is atomic per java doc: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html
// so even if multiple threads are doing this at once, whoever doing it last will overwrite it successfully.
s_lastResponse = r;
}
}
}
of course, if the question was any more complicated concurrency-wise (i.e. caching last 100 response instead of just 1), we would’ve needed locks, but given the simple requirement of this particular question…