Features:
Provide multi-reader, single writer object locking.
May lock both Managed objects and transactional POJOs.
Automatic deadlock detection.
Automatic rollback of modifications to transactional resources when a deadlock or error is encountered.
Cost:
Additional processing for each object that is locked. Additional processing and temporary heap space for each transactional field which is modified. Additional processing for deadlock detection. Temporarily blocked threads when there is transaction contention.
Usage:
Used to access Managed objects.
May be used to transactionally isolate and protect modifications to POJOs.
Used when multiple reader/single writer access to a resource is desired. Provides scalability for multiple readers executing simultaneously in multiple threads, while still providing data consistency through exclusive write locking.
Small transactions scale better than large transactions.
Avoid:
Using transactions to manage non-transactional resources (e.g. Network).
Using transactions when transactional semantics for a resource are not required (e.g a counter that needs to atomically increment but never rollback).
Deadlocks. Although the ActiveSpaces® Transactions runtime automatically rolls back and replays deadlocked transactions, this is very expensive compared to avoiding the deadlock entirely. If deadlocks are seen in your testing, the involved code should be re-organized or re-written to eliminate the possibility of deadlock.
Promotion locks. When two threads concurrently execute the same code path containing a promotion lock, a deadlock will be generated. Several different techniques can be used to eliminate promotion locks:
Changing the code to take a write lock instead of a read lock at the first access in the transaction to the Managed object to be modified.
When finding an object through a query, use either
LockMode.WRITELOCK
or
LockMode.NOLOCK
.
When iterating objects from ManagedObject.extent()
or KeyQuery.getResults()
use either
LockMode.WRITELOCK
or
LockMode.NOLOCK
.
When the modification of an already read-locked object does not
need to be done in the same transaction, move it to an
@Asynchronous
method and it will run in another
transaction after the current transaction commits.
Transaction lock contention. When a transaction is blocked waiting to acquire a lock, it remains blocked at least until the transaction holding the lock commits or aborts. It may remain blocked longer if there are multiple threads competing for the same transaction locks.
Long running transactions. Transactional resources in multi-threaded applications are generally shared between threads. Locking a resource in a long running transaction can block other threads for the duration of the transaction. Short running transactions scale better than long running transactions.
Large transactions (those that contain many locked resources). Large transactions tend to be more prone to generating contention and deadlocks. When there is contention between large transactions, even if there are no deadlocks, the deadlock detection becomes more expensive.
Summary:
Transactions are a powerful tool for maintaining application data consistency and scaling. But this feature comes at a cost. Avoid using transactions where they are not necessary.
Features:
Monitors (the Java synchronize keyword) provide a simple mutual exclusion mechanism.
Lighter weight than transactions.
Easy to cause undetected deadlocks.
Multiple threads sharing read access to a resource become single-threaded when accessing the resource.
Usage:
Use monitor when synchronization is required for non-transactional resources.
Avoid:
Using monitors on transactional resources (they are already protected by transaction locking).
Use of this isolation level carries a performance penalty. An extra shared memory copy of the object data must be made the first time the data is accessed with a transaction. Subsequent accesses then use the read image, and commit frees the memory.
The default isolation level, SERIALIZABLE, does not carry this penalty.