Transaction locks are taken in the following cases on a Managed Object:
Read locks are promoted to write locks if object fields are first read and then modified. Lock promotion can only occur if a serializable isolation level is being used in a transaction.
Transaction locks are held until the current transaction commits or aborts.
![]() | |
No locks are taken when a method is invoked. It is possible to execute a method on an object without taking any transaction locks if that method does not access any fields in the object. |
Example 4.8. Object locking
// $Revision: 1.1.2.1 $ package com.kabira.snippets.transactions; import com.kabira.platform.Transaction; import com.kabira.platform.ManagedObject; import com.kabira.platform.annotation.Managed; /** * Snippet showing transaction locking * <p> * <h2> Target Nodes</h2> * <ul> * <li> <b>domainnode</b> = A * </ul> */ public class Locking extends Transaction { @Managed public static class MyObject { private MyObject() { }; /** * Create MyObject * * @param name Initialize name field */ public MyObject(String name) { this.name = name; } /** * Name */ public String name; /** * Lock field */ public boolean lock; } /** * Transaction to create an instance of MyObject */ public static class CreateTransaction extends Transaction { private MyObject m_a; /** * Transaction run method */ @Override protected void run() { m_a = new MyObject("existing"); } } /** * Transaction to delete an instance of MyObject */ public static class DeleteTransaction extends Transaction { private MyObject m_a; /** * Transaction run method */ @Override protected void run() { // // Cache a reference to the object before deleting it // MyObject myObject = m_a; String name = myObject.name; if (Transaction.hasWriteLock(myObject) == false) { System.out.println(name + ": does not have a write lock"); } // // Deleting an object takes a write lock // ManagedObject.delete(m_a); if (Transaction.hasWriteLock(myObject) == true) { System.out.println(name + ": now has a write lock"); } } } private MyObject m_a; /** * Main entry point * @param args Not used */ public static void main(String[] args) { Locking locking = new Locking(); CreateTransaction createTransaction = new CreateTransaction(); DeleteTransaction deleteTransaction = new DeleteTransaction(); createTransaction.execute(); locking.m_a = createTransaction.m_a; locking.execute(); deleteTransaction.m_a = createTransaction.m_a; deleteTransaction.execute(); } /** * Transaction run method */ @Override protected void run() { MyObject a = new MyObject("created"); if (Transaction.hasWriteLock(a) == true) { System.out.println(a.name + ": has a write lock"); } // // This object does not have a write lock because it was created // outside of this transaction. Reading the name field will // take a read lock. // if (Transaction.hasWriteLock(m_a) == false) { System.out.println(m_a.name + ": does not have a write lock"); } if (Transaction.hasReadLock(m_a) == true) { System.out.println(m_a.name + ": now has a read lock"); } // // Take a write lock by setting the lock attribute. This // promotes the read lock taken above when name was read. // m_a.lock = true; if (Transaction.hasWriteLock(m_a) == true) { System.out.println(m_a.name + ": now has a write lock"); } } }
When Example 4.8, “Object locking” is run it generates the following output:
Example 4.9. Object locking output
[A] created: has a write lock [A] existing: does not have a write lock [A] existing: now has a read lock [A] existing: now has a write lock [A] existing: does not have a write lock [A] existing: now has a write lock
Deadlocks are handled transparently and do not have to be
explicitly handled by the application. When a deadlock occurs, the
Transaction
class detects the deadlock, rolls back the
current transaction and restarts a new transaction by calling the
run
method again.
It is possible to explicitly transaction lock objects. Explicit transaction locking is useful to avoid lock promotions. A lock promotion happens when an object has a read lock and then the object is modified. This is usually caused by first reading a field value and then modifying the object when using a serializable transaction isolation.
These mechanisms are available to explicitly lock objects:
Transaction.readLockObject
- using a
serializable isolation level, read locks an object. Performs a
snapshot when using read committed - snapshot isolation
level.
Transaction.writeLockObject
- explicitly
write lock an object
ManagedObject.extent(...,
- explicitly lock objects during extent
iterationLockMode
objectLock)
explicitly lock an object during queries
The example below show how to avoid a lock promotion.
Example 4.10. Avoiding lock promotion
// $Revision: 1.1.2.1 $ package com.kabira.snippets.transactions; import com.kabira.platform.Transaction; import com.kabira.platform.annotation.Managed; /** * Explicit object locking. * <p> * <h2> Target Nodes</h2> * <ul> * <li> <b>domainnode</b> = A * </ul> */ public class LockPromotion extends Transaction { @Managed public static class MyObject { String input; String output; } /** * Control program execution */ public enum Action { /** * Create the object */ CREATE, /** * Promote a read lock */ PROMOTE, /** * Take a write lock - avoiding lock promotion */ WRITELOCK } private MyObject m_b1; private Action m_action; /** * Main entry point * @param args Not used */ public static void main(String[] args) { LockPromotion lockPromotion = new LockPromotion(); lockPromotion.m_action = Action.CREATE; lockPromotion.execute(); lockPromotion.m_action = Action.PROMOTE; lockPromotion.execute(); lockPromotion.m_action = Action.WRITELOCK; lockPromotion.execute(); } /** * Report locks held on objects * @param msg message to include in lock report */ public void reportLock(String msg) { System.out.println(msg + " B1: read lock = " + Transaction.hasReadLock(m_b1) + ", write lock = " + Transaction.hasWriteLock(m_b1)); } /** * Transaction run method * * @throws com.kabira.platform.Transaction.Rollback */ @Override protected void run() throws Rollback { if (m_action == Action.CREATE) { m_b1 = new MyObject(); } else if (m_action == Action.PROMOTE) { reportLock("promote: enter"); // // Accessing input takes a read lock // String i = m_b1.input; reportLock("promote: read"); // // Read lock is promoted to write lock. Note this // also happens when the following is executed: // // m_b1.output = m_b1.input; // m_b1.output = i; reportLock("promote: write"); } else { assert (m_action == Action.WRITELOCK); reportLock("writelock: enter"); // // Explicitly take write lock to avoid promotion // Transaction.writeLockObject(m_b1); // // Accessing input will already have write lock // String i = m_b1.input; reportLock("writelock: read"); // // No promotion of locks happen // m_b1.output = i; reportLock("writelock: write"); } } }
When Example 4.10, “Avoiding lock promotion” is run it outputs the following (annotation added):
Example 4.11. Avoiding lock promotion output
[A] promote: enter B1: read lock = false, write lock = false # # Read lock is taken when field on B1 is read # [A] promote: read B1: read lock = true, write lock = false # # Write lock is taken when field on B1 is set # [A] promote: write B1: read lock = true, write lock = true [A] writelock: enter B1: read lock = false, write lock = false # # Explicitly write lock B1 causes both the read and write lock # to be taken on B1 # [A] writelock: read B1: read lock = true, write lock = true [A] writelock: write B1: read lock = true, write lock = true