java
46 lines · 7 steps
Retry with exponential backoff in Java
A reusable executor that retries failing operations with capped, jittered exponential backoff.
Explained by
highlit
1public final class RetryExecutor {
2
3 private final int maxAttempts;
4 private final Duration baseDelay;
5 private final Duration maxDelay;
6 private final Predicate<Throwable> retryable;
7
8 public RetryExecutor(int maxAttempts, Duration baseDelay, Duration maxDelay, Predicate<Throwable> retryable) {
9 this.maxAttempts = maxAttempts;
10 this.baseDelay = baseDelay;
11 this.maxDelay = maxDelay;
12 this.retryable = retryable;
13 }
14
15 public <T> T execute(Callable<T> operation) throws Exception {
16 Exception last = null;
17 for (int attempt = 1; attempt <= maxAttempts; attempt++) {
18 try {
19 return operation.call();
20 } catch (Exception e) {
21 last = e;
22 if (attempt == maxAttempts || !retryable.test(e)) {
23 throw e;
24 }
25 sleep(backoffWithJitter(attempt));
26 }
27 }
28 throw last;
29 }
30
31 private Duration backoffWithJitter(int attempt) {
32 long exp = baseDelay.toMillis() * (1L << (attempt - 1));
33 long capped = Math.min(exp, maxDelay.toMillis());
34 long jittered = ThreadLocalRandom.current().nextLong(capped + 1);
35 return Duration.ofMillis(jittered);
36 }
37
38 private void sleep(Duration delay) {
39 try {
40 Thread.sleep(delay.toMillis());
41 } catch (InterruptedException ie) {
42 Thread.currentThread().interrupt();
43 throw new CancellationException("retry interrupted");
44 }
45 }
46}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Exponential backoff spaces out retries so a struggling service isn't hammered on every attempt.
- 2Random jitter prevents many clients from retrying in lockstep and creating traffic spikes.
- 3Restoring the interrupt flag before throwing keeps cancellation semantics honest for callers upstream.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
java
package com.acme.billing.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties;
Feature-flagged beans with Spring @ConditionalOnProperty
feature-flags
conditional-beans
strategy-pattern
Intermediate
5 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/retry-with-exponential-backoff-in-java-explained-java-52a3/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.