What is the Gradle Worker API, and why would a custom task use it instead of just running its work directly in the task action?
answer
- @Inject WorkerExecutor
- WorkAction<WorkParameters>
- queue from noIsolation/classLoaderIsolation/processIsolation
- submit returns immediately
- Gradle awaits before task done
basics
~20 sThe Worker API lets a task hand units of work to Gradle's worker pool so they run in parallel. You inject WorkerExecutor, define a WorkAction, and submit work to a queue. It enables parallelism and isolation that a plain task action can't get.
solid answer
~40 sThe Worker API is Gradle's mechanism for splitting a task's work into discrete units (`WorkAction`s) submitted to a `WorkerExecutor` queue. Gradle runs those units in parallel using its build-wide worker thread/process pool, respecting `--max-workers`. Compared to running everything inline in `@TaskAction`, it gives you: (1) parallel execution of independent items even within one task; (2) isolation — `classLoaderIsolation` or `processIsolation` separate your code/deps from Gradle and other tasks; (3) better resource control because Gradle coordinates the worker budget across all running tasks. You inject `WorkerExecutor` via `@Inject`, get a queue from `noIsolation()`/`classLoaderIsolation()`/`processIsolation()`, and `submit(MyAction::class) { params... }`. The task action returns immediately; Gradle ensures the work completes before the task is done.
code
kotlin · 25 linesabstract class ReverseTask : DefaultTask() {
@get:Inject abstract val workerExecutor: WorkerExecutor
@get:InputFiles abstract val sources: ConfigurableFileCollection
@get:OutputDirectory abstract val outputDir: DirectoryProperty
@TaskAction
fun run() {
val queue = workerExecutor.noIsolation()
sources.forEach { f ->
queue.submit(ReverseAction::class.java) {
it.inputFile.set(f)
it.outputDir.set(outputDir)
}
}
}
}
interface ReverseParams : WorkParameters {
val inputFile: RegularFileProperty
val outputDir: DirectoryProperty
}
abstract class ReverseAction : WorkAction<ReverseParams> {
override fun execute() { /* do the work */ }
}go deeper
Know the three pieces: inject WorkerExecutor, implement WorkAction, submit to a queue for parallel work.
Explain the parameters-via-WorkParameters mechanism and that submit is non-blocking with Gradle awaiting automatically.
Contrast inline task work vs Worker API in terms of parallelism, isolation, and respecting the build-wide worker budget.
Frame it as the supported concurrency primitive for plugins — why Gradle owns the pool, and how it interacts with --max-workers and the configuration cache.
## What the Worker API is The **Worker API** is Gradle's supported way to run a task's work as independent **units of work** on a shared worker pool, instead of doing it all inline inside a `@TaskAction` method. It is the recommended replacement for spinning up your own threads/executors inside a task — Gradle owns the thread/process budget and coordinates it across the whole build. Two core types: - **`WorkerExecutor`** — a service Gradle injects into your task. You ask it for a **`WorkQueue`** with a chosen isolation mode. - **`WorkAction<P : WorkParameters>`** — an interface you implement with a single `execute()` method. It is the unit of work. Its parameters arrive via a typed `WorkParameters` object. ## Why not just run inline? A plain task action runs serially on the task's own thread. The Worker API adds three things: 1. **Parallelism within a task.** If a task processes 200 files, each file can be a `WorkAction` and Gradle runs them across worker threads up to `--max-workers`. 2. **Isolation.** `classLoaderIsolation()` runs the action in an isolated classloader (its own classpath, no leakage of Gradle internals); `processIsolation()` runs it in a separate JVM (own heap, JVM args). This is how, e.g., annotation processors or external tools run without polluting the build JVM. 3. **Coordinated resource use.** Because all tasks submit into the same pool, Gradle keeps total concurrency within the worker limit instead of every task spawning its own threads. ## The flow ```kotlin abstract class ReverseTask : DefaultTask() { @get:Inject abstract val workerExecutor: WorkerExecutor @get:InputFiles abstract val sources: ConfigurableFileCollection @get:OutputDirectory abstract val outputDir: DirectoryProperty @TaskAction fun run() { val queue = workerExecutor.noIsolation() sources.forEach { f -> queue.submit(ReverseAction::class.java) { it.inputFile.set(f) it.outputDir.set(outputDir) } } // task action returns; Gradle awaits the queue before the task finishes } } interface ReverseParams : WorkParameters { val inputFile: RegularFileProperty val outputDir: DirectoryProperty } abstract class ReverseAction : WorkAction<ReverseParams> { override fun execute() { val src = parameters.inputFile.asFile.get() // ... do the work } } ``` Key points: the parameters interface uses Gradle's lazy types (`Property`, `RegularFileProperty`, etc.); the `WorkAction` must be `abstract` (Gradle generates the `parameters` accessor); and `submit` returns immediately — the task action does not block on each unit. ## When the work completes Work submitted to a queue is asynchronous from the caller's view, but Gradle guarantees all submitted units finish before the **task** is considered complete — you don't normally need to wait explicitly. (You can force a wait mid-action with `await()`, covered separately.)
- Does submitting to a no-isolation queue actually run work in parallel?Yes — even noIsolation work runs on Gradle's worker threads and can execute concurrently with other submitted units, up to --max-workers. The only thing noIsolation skips is classloader/process separation, not parallelism.
- How do parameters get into the WorkAction?Through a typed interface extending WorkParameters whose properties are Gradle lazy types. You set them in the submit { } block; Gradle serializes/snapshots them and exposes them via the action's generated parameters property.
Think of the task action as a foreman handing slips of work to a shared crew (Gradle's worker pool) rather than doing every job personally — the crew is sized once for the whole site, not per foreman.
saying these in an interview costs you the question
- Saying the Worker API is only for separate processes — noIsolation and classLoaderIsolation also exist and run in-JVM.
- Claiming you must call await() to make work run — Gradle awaits the queue automatically before the task finishes.
- Spawning raw Java threads/ExecutorService inside a task instead of using the worker pool.