@@ -56,6 +56,7 @@ public Step sampleStep(TaskExecutor taskExecutor, JobRepository jobRepository, P
5656 return new StepBuilder("sampleStep", jobRepository)
5757 .<String, String>chunk(10).transactionManager(transactionManager)
5858 .reader(itemReader())
59+ .processor(itemProcessor())
5960 .writer(itemWriter())
6061 .taskExecutor(taskExecutor)
6162 .build();
@@ -83,36 +84,21 @@ is a standard Spring interface, so consult the Spring User Guide for details of
8384implementations. The simplest multi-threaded `TaskExecutor` is a
8485`SimpleAsyncTaskExecutor`.
8586
86- The result of the preceding configuration is that the `Step` executes by reading, processing,
87- and writing each chunk of items (each commit interval) in a separate thread of execution.
88- Note that this means there is no fixed order for the items to be processed, and a chunk
89- might contain items that are non-consecutive compared to the single-threaded case.
87+ The result of the preceding configuration is that the `Step` will use multiple threads from
88+ the task executor to process items concurrently. Therefore, the `ItemProcessor` will be
89+ called from multiple threads at the same time. This means that the `ItemProcessor` must be
90+ thread-safe. If you are using stateful components in the processing, you must ensure that
91+ they are properly synchronized for concurrent access.
92+
93+ The reading and writing of items is still done in serial by the main thread executing the step,
94+ so the `ItemReader` and `ItemWriter` do not have to be thread-safe or synchronized. However,
95+ the throughput of the step may be limited by the speed of reading and writing. If this is
96+ the case, consider using a different concurrency technique, such as local chunking or local partitioning.
9097
9198Note also that there may be limits placed on concurrency by any pooled resources used in
9299your step, such as a `DataSource`. Be sure to make the pool in those resources at least
93100as large as the desired number of concurrent threads in the step.
94101
95- There are some practical limitations of using multi-threaded `Step` implementations for
96- some common batch use cases. Many participants in a `Step` (such as readers and writers)
97- are stateful. If the state is not segregated by thread, those components are not
98- usable in a multi-threaded `Step`. In particular, most of the readers and
99- writers from Spring Batch are not designed for multi-threaded use. It is, however,
100- possible to work with stateless or thread safe readers and writers, and there is a sample
101- (called `parallelJob`) in the
102- https://github.com/spring-projects/spring-batch/tree/main/spring-batch-samples[Spring
103- Batch Samples] that shows the use of a process indicator (see
104- xref:readers-and-writers/process-indicator.adoc[Preventing State Persistence]) to keep track
105- of items that have been processed in a database input table.
106-
107- Spring Batch provides some implementations of `ItemWriter` and `ItemReader`. Usually,
108- they say in the Javadoc if they are thread safe or not or what you have to do to avoid
109- problems in a concurrent environment. If there is no information in the Javadoc, you can
110- check the implementation to see if there is any state. If a reader is not thread safe,
111- you can decorate it with the provided `SynchronizedItemStreamReader` or use it in your own
112- synchronizing delegator. You can synchronize the call to `read()`, and, as long as the
113- processing and writing is the most expensive part of the chunk, your step may still
114- complete much more quickly than it would in a single-threaded configuration.
115-
116102[[scalabilityParallelSteps]]
117103== Parallel Steps
118104
0 commit comments