Concurrent lazy variables
In addition to using the Java native concurrency classes, Gosu includes utility classes that provide additional concurrency functionality. The LockingLazyVar class implements what is known as a lazy variable. Gosu constructs a lazy variable the first time some code uses the variable and not at the variable declaration. Because the LockingLazyVar class uses the Java concurrency libraries, access to the lazy variable is thread-safe. The LockingLazyVar class wraps the double-checked locking pattern in a type-safe holder.
In Gosu, the LockingLazyVar.make(gw.util.concurrent.LockingLazyVar.LazyVarInit)
method signature returns the lazy variable object. This method requires
a Gosu block that creates an object. Gosu runs this block on the first
access of the LockingLazyVar
value. The following example demonstrates the use of this method and
clarifies the method signature:
var _lazy = LockingLazyVar.make( \-> new ArrayList<String>() )The example passes a block as an argument to LockingLazyVar.make(...). That
block creates a new ArrayList that is parameterized to the
String class. The parameter is a block that creates a new object. In this
case, the block returns a new ArrayList object. You can create any object. In
your business-use code, this block might be very resource-intensive in creating or loading
this object.
It is best to let Gosu infer the correct type of the block and the result of the make method, as shown in this example. Using Gosu type inference simplifies your code because you do not need to use explicit Gosu generics syntax to define the block type, such as the following version:
var _lazy : LockingLazyVar<List<String>> = LockingLazyVar.make( \-> new ArrayList<String>() )To use the lazy variable, call its get method:
var i = _lazy.get()If the block has not yet run, Gosu runs the block when you access the lazy variable. If the block has already run, Gosu returns the cached value of the lazy variable and does not rerun the block.
A good approach to using a lazy variable is to define the variable as static and then define a property accessor function to abstract the implementation of the variable. The following code shows an example inside a Gosu class definition:
class MyClass {
// Lazy variable using a block that calls a resource-intensive operation that retuns a String
var _lazy = LockingLazyVar.make( \-> veryExpensiveMethodThatRetunsAString() )
// Define a property get function that gets this value
property get MyLazyString() : String {
return _lazy.get()
}
}
If any code accesses the property MyLazyString, Gosu calls its property
accessor function. The property accessor always calls the get method on the object. Gosu
runs the very expensive method only once, the first time that code accesses
the lazy variable value. If any code accesses this property again, Gosu
uses the cached value and does not execute the block again. This behavior
is useful in cases where you want a system to start quickly and pay only
incremental costs for resource-intensive value calculations.
Optional non-locking lazy variables
Gosu also provides a non-locking variant of LockingLazyVar called
LocklessLazyVar. Use LocklessLazyVar if you do not need
the code to be thread-safe. Because this class does not lock, it performs faster and has
less chance of server deadlock. However, this class is unsafe in any context that has
potential concurrent access and thus requires thread safety.
