Concurrency with monitor locks and reentrant objects
From Gosu, you can use the Java 1.5 concurrency
classes in the package java.util.concurrent
to synchronize a variable’s data to prevent simultaneous access to
the data.
The most straightforward implementation
of a lock is to define a static variable for the lock in your class definition.
Next, define a property get accessor function that uses the lock and
calls another method to perform the task that you must synchronize. This
approach uses a Gosu using
clause with reentrant objects to simplify concurrent access to shared
data. The using statement
syntax for lock objects causes Gosu to consider the object reentrant.
A reentrant object supports multiple concurrent uses of a single lock in a single thread. Reentrancy maintains a count of the number of times the thread sets and releases the lock. In reentrant lock use, the thread does not deadlock on setting an already locked lock, nor release the lock when other code in the thread still retains that lock. Reentrant objects help manage safe access to data that is shared by reentrant or concurrent code execution. For example, if you must store data that is shared by multiple threads, ensure that you protect against concurrent access from multiple threads to prevent data corruption. The most prominent type of shared data is class static variables, which are variables that are stored on the Gosu class itself.
For Gosu to recognize a valid reentrant object, the object must have at least one of the following attributes:
- The object implements the
java.util.concurrent.locks.Lockinterface. For example, the following Java classes in that package implement this interface:ReentrantLock,ReadWriteLock,Condition. - You cast the object to the Gosu interface
IMonitorLock. You can cast any arbitrary object toIMonitorLock. It is useful to cast Java monitor locks to this Gosu interface. - The object implements the Gosu class gw.lang.IReentrant. This
interface contains two methods with no arguments:
enterandexit. Your implementation code must properly lock or synchronize data access as appropriate during the enter method and release any locks in the exit method.The profiler tag class (gw.api.profiler.ProfilerTag) implements the IReentrant interface. This class adds hints to the Gosu profiler about what actions happen in a block of code.
For blocks of code that use locks by implementing
java.util.concurrent.locks.Lock, a using clause
simplifies the code. The using statement always cleans up the lock, even if
the code throws an exception.
uses java.util.concurrent
// In your class variable definitions...
var _lock : ReentrantLock = new ReentrantLock()
...
function useReentrantLockNew() {
using ( _lock ) {
// Do your main work here
}
}
Similarly, you can cast any object to a monitor lock by adding
as IMonitorLock after the object. For example, the following method code
uses the object itself, by using the keyword this, as the monitor lock:
function monitorLock() {
using ( this as IMonitorLock ) {
// Do stuff
}
}
This approach is equivalent to a synchronized block in the
Java language.
The following code uses the java.util.concurrent.locks.ReentrantLock
class using more verbose try and finally statements. This
approach is not recommended:
// In your class variable definitions...
var _lock : ReentrantLock = new ReentrantLock()
function useReentrantLockOld() {
_lock.lock()
try {
// Do your main work here
}
finally {
lock.unlock()
}
}
Alternatively, you can do your change in a Gosu block. Guidewire does not recommend this
approach. Returning the value from a block imposes more restrictions on how you implement
return statements. It is typically better to use the
using statement structure shown earlier in this topic.
See also
