|
| 1 | +# QuickFIX/J Threading Model — Developer Guide |
| 2 | + |
| 3 | +QuickFIX/J provides two threading strategies for processing FIX messages. The choice of strategy |
| 4 | +affects how your application handles concurrent sessions and what thread-safety guarantees you must |
| 5 | +provide. |
| 6 | + |
| 7 | +## Threading Strategies |
| 8 | + |
| 9 | +### Single-Threaded Strategy |
| 10 | + |
| 11 | +**Classes:** `SocketAcceptor` / `SocketInitiator` |
| 12 | + |
| 13 | +All sessions share a single message-processing thread (named `QFJ Message Processor`). Incoming |
| 14 | +messages from all sessions are placed in a shared queue and dispatched one at a time by this thread. |
| 15 | + |
| 16 | +This means your `Application` callbacks (`fromApp`, `fromAdmin`, etc.) are always invoked from the |
| 17 | +same thread, so you do not need to make your application code thread-safe with respect to concurrent |
| 18 | +session callbacks. However, a slow callback will delay message processing for all other sessions. |
| 19 | + |
| 20 | +**Use when:** |
| 21 | +- You have a small number of sessions. |
| 22 | +- Simplicity and predictable, sequential message processing are more important than throughput. |
| 23 | +- You want to avoid the complexity of thread-safe application code. |
| 24 | + |
| 25 | +### Thread-Per-Session Strategy |
| 26 | + |
| 27 | +**Classes:** `ThreadedSocketAcceptor` / `ThreadedSocketInitiator` |
| 28 | + |
| 29 | +Each session gets its own dedicated message-dispatching thread. Incoming messages for a session are |
| 30 | +queued and processed by that session's thread independently of other sessions. |
| 31 | + |
| 32 | +Because your `Application` callbacks can be invoked concurrently from multiple session threads, your |
| 33 | +application code **must be thread-safe**. |
| 34 | + |
| 35 | +**Use when:** |
| 36 | +- You have multiple sessions and need them to process messages independently. |
| 37 | +- A slow or blocking callback for one session must not impact other sessions. |
| 38 | +- You can ensure your application implementation is thread-safe. |
| 39 | + |
| 40 | +## Queue Capacity and Back-pressure |
| 41 | + |
| 42 | +Both strategies support configuring the internal message queue capacity to control back-pressure: |
| 43 | + |
| 44 | +```java |
| 45 | +// Fixed-capacity queue (blocks producers when full) |
| 46 | +Acceptor acceptor = new ThreadedSocketAcceptor( |
| 47 | + application, storeFactory, settings, logFactory, messageFactory, |
| 48 | + queueCapacity); |
| 49 | + |
| 50 | +// Watermark-based flow control |
| 51 | +Acceptor acceptor = ThreadedSocketAcceptor.newBuilder() |
| 52 | + .withApplication(application) |
| 53 | + .withMessageStoreFactory(storeFactory) |
| 54 | + .withSettings(settings) |
| 55 | + .withLogFactory(logFactory) |
| 56 | + .withMessageFactory(messageFactory) |
| 57 | + .withQueueLowerWatermark(lowerWatermark) |
| 58 | + .withQueueUpperWatermark(upperWatermark) |
| 59 | + .build(); |
| 60 | +``` |
| 61 | + |
| 62 | +The same constructors and builder options are available on `SocketAcceptor`, `SocketInitiator`, and |
| 63 | +`ThreadedSocketInitiator`. |
| 64 | + |
| 65 | +## Choosing a Strategy |
| 66 | + |
| 67 | +| | `SocketAcceptor` / `SocketInitiator` | `ThreadedSocketAcceptor` / `ThreadedSocketInitiator` | |
| 68 | +|---|---|---| |
| 69 | +| Message processing | Single shared thread | One thread per session | |
| 70 | +| Application thread-safety required | No | Yes | |
| 71 | +| Session isolation | No | Yes | |
| 72 | +| Typical use case | Few sessions, simple apps | Many sessions, independent processing | |
| 73 | + |
| 74 | +## Example: Starting an Acceptor |
| 75 | + |
| 76 | +```java |
| 77 | +import quickfix.*; |
| 78 | +import java.io.FileInputStream; |
| 79 | + |
| 80 | +public class MyApp { |
| 81 | + |
| 82 | + public static void main(String[] args) throws Exception { |
| 83 | + Application application = new MyApplication(); |
| 84 | + SessionSettings settings = new SessionSettings(new FileInputStream(args[0])); |
| 85 | + MessageStoreFactory storeFactory = new FileStoreFactory(settings); |
| 86 | + LogFactory logFactory = new FileLogFactory(settings); |
| 87 | + MessageFactory messageFactory = new DefaultMessageFactory(); |
| 88 | + |
| 89 | + // Single-threaded: all sessions share one message-processing thread |
| 90 | + Acceptor acceptor = new SocketAcceptor( |
| 91 | + application, storeFactory, settings, logFactory, messageFactory); |
| 92 | + |
| 93 | + // OR thread-per-session: each session has its own message-processing thread |
| 94 | + // (application must be thread-safe) |
| 95 | + // Acceptor acceptor = new ThreadedSocketAcceptor( |
| 96 | + // application, storeFactory, settings, logFactory, messageFactory); |
| 97 | + |
| 98 | + acceptor.start(); |
| 99 | + // ... run your application ... |
| 100 | + acceptor.stop(); |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +## Thread Safety Guidance |
| 106 | + |
| 107 | +Regardless of which strategy you choose, note that `Session.sendToTarget()` is thread-safe and may |
| 108 | +be called from any thread to send outgoing messages. |
| 109 | + |
| 110 | +When using `ThreadedSocketAcceptor` or `ThreadedSocketInitiator`, ensure that any shared state |
| 111 | +accessed in your `Application` implementation (e.g., order books, maps, counters) is properly |
| 112 | +synchronized or uses thread-safe data structures. |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +*For a deep technical reference on the threading internals, see [`threading-model.md`](./threading-model.md).* |
0 commit comments