In the source code of java.util.Scanner
I've found these static utility methods:
private static Pattern separatorPattern() {
Pattern sp = separatorPattern;
if (sp == null)
separatorPattern = sp = Pattern.compile(LINE_SEPARATOR_PATTERN);
return sp;
}
private static Pattern linePattern() {
Pattern lp = linePattern;
if (lp == null)
linePattern = lp = Pattern.compile(LINE_PATTERN);
return lp;
}
Why it was done in such a complex way and not just, say,
private static Pattern linePattern() {
if (linePattern == null)
linePattern = Pattern.compile(LINE_PATTERN);
return linePattern;
}
What was the point of using a local variable (lp
) here? Is this some kind of optimization technique? Or maybe precaution against a concurrent modification? But linePattern
cannot be set to null
again as this method is the only place where it is modified.
I believe it's all about volatile
keyword used with linePattern
and separatorPattern
fields
private static volatile Pattern separatorPattern;
private static volatile Pattern linePattern;
Due to specification
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.
A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable
Also here we can read that
Declaring a volatile Java variable means:
The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory";
Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.
That's why they firstly try to directly read field's value and then overwrite it with new value
I think they want to avoid reading from the volatile
field twice.
null
return
In their version, they only
The optimization is for the "happy path" that happens incredibly more often than the "initial setup path" that only happens once -- or at most a few times if the method is called concurrently before it has finished the initialization.
Or maybe precaution against a concurrent modification?
There is no such precaution. If you call linePattern
concurrently before it has finished setting the static volatile field, the pattern will be created multiple times (but that's fine), different instances will be returned and a random one of those will be chosen to stick around (that's also fine, since they are equivalent).
Any measures to guard against that would only add cost to our "happy path" so should only be done in situations were the instance must really be a singleton for some reason.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With