This page explains the most important things you need to understand about concurrent threads and their activities that could interfere (or not).

Important Threads

Each transaction (and coordinator) has a number of threads that act on it:

Thread Description
The application thread The thread that starts the transaction, does work within its scope and then terminates the transaction.
The timer thread The thread responsible for monitoring timeout of the transaction. This thread is started alongside the transaction and runs in the background.
The two-phase commit thread(s) Imported transactions are subject to two-phase commit termination by the remote initiator of the transaction. As per standard remoting practices this typically involves separate threads different from the application thread. In normal cases, the remote will start termination only after all application threads have finished. However, there are special cases that involve pending "orphans" for which the remote got a network timeout. Our code checks for this and rejects incoming two-phase commit requests when a local application thread is still active for the transaction.
The main thread The thread that starts and stops the transaction service.

The TransactionService Class

Our TransactionService class is the main container class for transaction, coordinator and root information. It maintains collections (maps, actually) that allow retrieval of these objects based on their IDs.

In order to avoid memory leaks, it is crucial that all additions to those maps are eventually followed by a removal. While this sounds easy, it can be tricky when there are race conditions of this form:

  1. An application thread starts a transaction and is paused by the JVM
  2. A concurrent timer thread or two-phase commit thread terminate the transaction and remove its information from the TransactionService's maps - typically by some form of rollback.
  3. The application then resumes its work from step 1 above, and inserts transaction information in the map - when it is no longer needed. This will keep useless information in the map.

In order to avoid this, our design needs to respect one main invariant:

Design Invariant 1: Concurrency

There is no meaningful manipulation of transaction state by other threads, as long as the application thread has not completed its work on behalf of a transaction. Completion of this work is signalled by the application thread's calls to either rollback or commit methods on the transaction. Upon completion, the application thread has finished its work for the transaction, meaning it will no longer attempt to call methods that add transaction information in any of the TransactionService's maps.

More precisely, the only state change allowed by other threads during this time lapse is marking the transaction for rollback as the only allowed outcome.

This invariant vastly simplifies the threading model and the code. The trade-off is that application threads must properly call either commit or rollback for every transaction they start, whatever exceptions occur.

Design Invariant 2: Shutdown

During or after system shutdown, no new transactions are allowed to start. This is indicated by a shutdown flag, set by the shutdown method.

Concurrency Model Overview

The following table summarises the concurrent activities by all relevant threads from the point of view of the TransactionService class, with abbreviations meaning:
  • tid = the transaction ID
  • coordId = the coordinator ID
  • rootId = root ID

Method Application Thread(s) Timer Thread 2 Phase Commit Thread(s) Main Thread
getCompositeCoordinator READ ONLY ACCESS
getParticipant READ ONLY ACCESS
getCompositeTransaction READ ONLY ACCESS
createCompositeTransaction READS rootId - - -
ADDS rootId, coordId, tid - - (**)
recreateCompositeTransaction READS rootId - - -
ADDS rootId, coordId, tid - - (**)
createSubTransaction READS rootId - - -
ADDS rootId, coordId, tid - - (**)
committed REMOVES tid - - -
rolledback REMOVES tid - - -
entered REMOVES coordId, rootId REMOVES coordId, rootId (*) REMOVES coordId, rootId (*) -
shutdown - - - Sets shutdown flag

(*) The entered method notifies the system of a final state of a transaction being reached - so it can be forgotten. By design invariant 1, other threads' removals will never interfere with concurrent additions by the application threads because all other state-changing methods are called during the active life of a transaction, never afterwards.

(**) These methods will fail (without state changes) after the shutdown flag was set by the main thread - by design invariant 2. Note that removal methods are allowed during shutdown, because shutdown tends to wait for active transactions to finish.

The CompositeTransactionManager Class

This class contains only logic that is per-transaction and relevant only during the transaction's active lifetime. As such, there is no concurrency beyond regular application threads - each acting on transactions limited to their own thread's scope and not likely to interfere with each other in significant ways.

-- Guy Pardon - 15 Mar 2024

Corporate Information

Atomikos Corporate Headquarters
Hoveniersstraat, 39/1, 2800
Mechelen, Belgium

Contact Us

Copyright 2026 Atomikos BVBA | Our Privacy Policy
By using this site you agree to our cookies. More info. That's Fine