Java Reference
In-Depth Information
public WordUsage withWord(String word) {
return new WordUsage(textName, lineOffset, word, wordOffset);
}
public int getWordOffset() {
return wordOffset;
}
public WordUsage withWordOffset(int wordOffset) {
return new WordUsage(textName, lineOffset, word, wordOffset);
}
}
The immutability of the Java bean in Listing 5-1 is significant, because streams can quickly (and often
transparently) move into processing on multiple threads. The Java memory model does not guarantee that
an object constructed in Thread A but accessed in Thread B will necessarily have its fields intact. If you
construct an object in one thread and access it in another, any given field access may return null , even if
you assign it in the constructor. The reason is because different threads maintain their own distinct memory
space, and the Java virtual machine will take a significant performance hit if it has to walk the entire tree and
copy the objects whenever a thread modifies a field. The really obnoxious part about this is that this rule is
poorly understood, and the error may not crop up until the system is under load, at which point the system
is a rather unhelpful NullPointerException that appears where it should be impossible. That's a nightmare
for a poor developer to debug, especially if the concurrency is obscured.
To solve this problem, the Java virtual machine gives you three options. First, you can use a synchronized
block on every object that you access. This is unwieldy and error-prone. Your other two options are to
annotate the field with volatile or final , either of which will solve the memory management problem.
The volatile keyword tells Java that the runtime needs to check its thread-local cache against the source
of truth every time you access the field. This is a bit of a performance hit, but it works as long as the object's
fields are themselves thread-safe. The final keyword tells Java that the field cannot change value, and so
whatever value you find at one point will be the value in the future. Since the field cannot change value,
there's no need for the run time to be copying the field around the caches or checking the cache against
the source of truth. In addition to solving the memory management problem without the performance hit
of volatile , it also makes the object much easier to reason about, because each instance of the class has
a fixed, permanent value. If the field values of an object don't change, then there is nothing that another
thread can do to break your thread's reasoning about the object, so you can pretend as though you are in a
single thread of execution even in a multi-threaded environment.
Whereas old style Java objects are referred to as POJOs (“Plain Old Java Objects”), objects in this new
immutable style are sometimes called PIJOs (“Plain Immutable Java Objects”), pronounced “PEE-johs.”
Personally, I prefer the term FuJIs (“Functional Java Instances”).
Note that FuJIs fit better with streams than POJOs do. Whereas as POJO's setter has a type that takes an
argument and returns void, FuJI's “setters” return the type that was being modified. This means that you can
use the setter as a mapping in the stream. For instance, if you wanted to set the text name to a single value
for all the entries of the stream, the FuJI code is notably shorter and more readable than the POJO code, as
demonstrated in Listing 5-2.
 
Search WWH ::




Custom Search