BlockHound for Detecting Blocking Calls in Reactor Applications
Java agent to detect blocking calls from non-blocking threads.
At a glance
- What is it?
- BlockHound is a Java agent that instruments the JVM and reports blocking operations performed by threads marked for non-blocking work. It suits Reactor developers who want errors to identify the exact call that blocked a scheduler thread.
- Who is it for?
- Adopt BlockHound in Reactor or RxJava 2 test suites when blocking calls on scheduler threads need immediate detection. Do not treat it as a complete performance monitor or a substitute for load testing.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- reactor/BlockHound1,511 ★ · +2
| Repository | 2026-10-16 | 2026-11-07 |
|---|---|---|
| reactor/BlockHound | 1.5k | 1.5k |
2 Stars in the last 22 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
BlockHound Targets Reactive Scheduler Mistakes
BlockHound solves a specific class of concurrency mistakes in reactive Java applications. It detects blocking calls made from threads that are marked as being for non-blocking operations only. The README identifies Reactor threads started by `Schedulers.parallel()` as an example, along with threads implementing Reactor's `NonBlocking` marker interface. In this setting, a call such as `Thread.sleep`, synchronous I/O, or another blocking JVM operation can consume a thread that the reactive scheduler expects to keep available.
The intended users are developers building applications with Project Reactor and developers using supported RxJava integrations. The project is not a general-purpose thread monitor. Its purpose is to turn a difficult scheduling violation into an error that identifies the call responsible. That makes it more useful as a diagnostic or test-time guard than as a production telemetry system.
BlockHound does not prevent every kind of latency or resource waste. It reacts to calls that its instrumentation recognizes as blocking and that occur on threads carrying the relevant non-blocking marker. The README does not claim complete detection for every blocking library, thread pool, or reactive framework. That boundary matters when interpreting a passing test. It may show that the tested paths did not trigger BlockHound, but it does not prove that every possible blocking operation was removed.
Transparent JVM Instrumentation and Source-Level Errors
According to the README, BlockHound transparently instruments JVM classes after installation. It intercepts blocking calls, including I/O calls, when they are made from a thread marked as non-blocking. The interception produces a `reactor.blockhound.BlockingOperationError`, and the exception includes the blocking method and the application location that triggered it.
The documented example installs the agent, delays work through `Mono.delay`, and calls `Thread.sleep` inside a `doOnNext` callback. The reported trace names `java.lang.Thread.sleep` and points to the line in `Example.java` that invoked it. This source-location detail is a practical distinction between BlockHound and a generic alert that only says a non-blocking thread appears slow. It gives the developer a concrete place to inspect the call chain.
The architecture depends on instrumentation rather than a source-code rewrite in the application. BlockHound also provides an SPI for integrations. Project Reactor 3.2.x is supported out of the box. Starting with `reactor-core` 3.3.0, Reactor contains an integration that uses BlockHound's SPI. RxJava 2 is also listed as supported, while RxJava 3 and later require an SPI implementation from either the framework or the user. Framework coverage therefore depends on the reactive library version and its available integration.
Adding BlockHound to Gradle and Maven Tests
The README recommends obtaining stable releases from Maven Central. Milestone and snapshot repositories are also shown, but the examples use a `$LATEST_RELEASE` placeholder rather than a fixed release number. The dependency is normally added to the test configuration so that the agent is present while tests exercise reactive code.
For a Gradle project, the documented setup is:
repositories {
mavenCentral()
}
dependencies {
testImplementation 'io.projectreactor.tools:blockhound:$LATEST_RELEASE'
}Maven Central supplies the stable artifact. The commented repository alternatives in the README point to `https://repo.spring.io/milestone` and `https://repo.spring.io/snapshot` for developers who intentionally need those release channels.
The Kotlin DSL form uses the same artifact and test configuration:
repositories {
mavenCentral()
}
dependencies {
testImplementation("io.projectreactor.tools:blockhound:$LATEST_RELEASE")
}For Maven, the README gives this dependency declaration:
<dependencies>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound</artifactId>
<version>$LATEST_RELEASE</version>
</dependency>
</dependencies>The dependency declaration alone does not show the complete application wiring. The README's Java example also calls `BlockHound.install()`. A new project should confirm both the build dependency and the installation point before treating BlockHound as active.
A First Reactor Test and the JDK 13+ Requirement
The README's first example starts with an explicit installation call and deliberately performs a blocking operation inside a Reactor callback:
BlockHound.install();
Mono.delay(Duration.ofSeconds(1))
.doOnNext(it -> {
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
})
.block();According to the example, this results in a `reactor.blockhound.BlockingOperationError` naming `java.lang.Thread.sleep`. The trace also includes the application line where the blocking call occurred. This makes the example a useful initial test because it checks agent activation and error reporting without requiring an external service.
For JDK 13 and later, the README documents a temporary JVM workaround because redefining native methods is no longer allowed by default. Maven users can add the argument to `maven-surefire-plugin`:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
</configuration>
</plugin>The corresponding Gradle configuration conditionally adds the argument for Java 13 or newer:
tasks.withType(Test).all {
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_13)) {
jvmArgs += [
"-XX:+AllowRedefinitionToAddDeleteMethods"
]
}
}The reader should inspect the effective test configuration to confirm that the selected test task receives this argument. The README calls the option a temporary workaround, so its continued relevance should be checked against the JDK version used by the project.
Integration Boundaries and Diagnostic Limits
BlockHound is useful where the application's reactive boundary is known and its libraries expose compatible integration points. The built-in integrations listed in the README cover Project Reactor 3.2.x and RxJava 2. Reactor 3.3.0 and later use an SPI-based integration, while RxJava 3 and later require an SPI implementation. A library that blocks internally but is not covered by an integration may not produce the precise diagnostic that the project is designed to provide. The SPI mechanism addresses that extension need, but the README does not enumerate integrations for every blocking library.
The agent also depends on thread classification. Its detection applies to threads marked as non-blocking operations only. A blocking call on an ordinary worker thread is outside the stated target condition. A passing test suite therefore does not prove that all blocking work has been removed. It shows that the exercised paths did not trigger BlockHound under the configured thread markers and integrations.
BlockHound should not be selected as a replacement for thread dumps, latency tracing, or load tests. Those tools address different questions. BlockHound is suited to failing fast when a blocking call violates the scheduling contract of a non-blocking thread. The README does not document production safety policies, allowlists, configuration controls, rollback procedures, or a report format beyond the exception shown in its example. Teams requiring those controls would need answers from documentation not present in the README.
Built-in Reactor Integration Versus a Custom SPI
A real alternative is to add custom reactive framework integration through BlockHound's SPI rather than relying only on the listed built-in integrations. BlockHound supplies the Java agent and a mechanism for identifying additional blocking integrations. Project Reactor 3.2.x is described as supported out of the box, while `reactor-core` 3.3.0 introduced a built-in Reactor integration that uses that SPI. The distinction is one of responsibility: BlockHound performs JVM instrumentation and defines the integration mechanism, while Reactor participates in recognizing its own non-blocking schedulers.
For an application using a compatible Reactor version, the built-in Reactor integration is the path to verify first. For RxJava 2, the README lists built-in support. RxJava 3 and later require an SPI implementation supplied by the framework or written by the user. That differs materially from adding BlockHound and assuming every reactive framework is covered.
The practical choice is between using the documented integrations and accepting their framework and version boundaries, or implementing the SPI for an unsupported library. Projects with a custom blocking stack may need to supply their own integration. Projects using the listed compatible combinations can begin with the documented dependency, installation call, and test configuration.
Maintenance, Releases, and Apache-2.0 Licensing
BlockHound is not archived. Its last push was on 2026-10-14. Recent releases include `1.0.17.RELEASE` on 2026-06-08, `1.0.16.RELEASE` on 2026-01-13, and `1.0.15.RELEASE` on 2025-10-14. The README links to Maven Central for stable releases and to `repo.spring.io` for milestone and snapshot artifacts.
The project is licensed under Apache Software License 2.0. That identifies the license selected by the repository, but the `LICENSE` file should be reviewed for obligations that apply to a particular distribution or integration. This review does not provide legal advice or interpret how the license applies to a user's product.
The main upgrade cost documented by the project is compatibility work. Users may need to revisit the JDK 13+ test JVM argument, confirm that the Reactor or RxJava version has the expected integration, and determine whether an unsupported framework now needs an SPI implementation. The README points to `docs/custom_integrations.md` for integration work and `docs/README.md` for the quick start. It does not provide a detailed upgrade procedure or compatibility matrix beyond the versions and integrations listed in its text.
Editorial conclusion
Adopt BlockHound in Reactor or RxJava 2 test suites when blocking calls on scheduler threads need immediate detection. Do not treat it as a complete performance monitor or a substitute for load testing. Verify the reactive library integration, JDK configuration, and SPI coverage first. The documented first check is whether the agent reports the offending blocking method and its source location.
Frequently asked questions
How do I install BlockHound in a Java project?
Add `io.projectreactor.tools:blockhound:$LATEST_RELEASE` as a test dependency, then call `BlockHound.install()` in the setup described by the README. For JDK 13+, the README also documents `-XX:+AllowRedefinitionToAddDeleteMethods` as a temporary workaround.
Which reactive frameworks does BlockHound support?
The README lists Project Reactor 3.2.x as supported out of the box and says Reactor 3.3.0 added a built-in SPI-based integration. RxJava 2 is supported, while RxJava 3 and later require an SPI implementation.
What happens when BlockHound detects a blocking call?
BlockHound throws a `reactor.blockhound.BlockingOperationError` from the intercepted call. The documented trace identifies the blocking method and the application source location that triggered it.
Does BlockHound detect blocking calls on every Java thread?
The README says detection applies to blocking calls made from threads marked as non-blocking operations only, such as threads implementing Reactor's `NonBlocking` interface. It does not claim to flag every blocking operation on every thread.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/reactor-blockhound)Star history chart for your README
[](https://hysenlabs.com/projects/reactor-blockhound)Was this useful?