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