diff --git a/patterns/backoff-jitter/pom.xml b/patterns/backoff-jitter/pom.xml
new file mode 100644
index 0000000000..0a48a18abb
--- /dev/null
+++ b/patterns/backoff-jitter/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+ com.baeldung
+ backoff-jitter
+ 1.0.0-SNAPSHOT
+ pom
+
+
+
+
+
+
+
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito-core.version}
+ test
+
+
+ io.github.resilience4j
+ resilience4j-retry
+ ${resilience4j.version}
+ test
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+ test
+
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+ 4.12
+ 2.27.0
+ 1.7.26
+ 0.16.0
+
+
+
\ No newline at end of file
diff --git a/patterns/backoff-jitter/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterTest.java b/patterns/backoff-jitter/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterTest.java
new file mode 100644
index 0000000000..aef9f7fab7
--- /dev/null
+++ b/patterns/backoff-jitter/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterTest.java
@@ -0,0 +1,81 @@
+package com.baeldung.backoff.jitter;
+
+import io.github.resilience4j.retry.IntervalFunction;
+import io.github.resilience4j.retry.Retry;
+import io.github.resilience4j.retry.RetryConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
+
+import static com.baeldung.backoff.jitter.BackoffWithJitterTest.RetryProperties.*;
+import static io.github.resilience4j.retry.IntervalFunction.ofExponentialBackoff;
+import static io.github.resilience4j.retry.IntervalFunction.ofExponentialRandomBackoff;
+import static java.util.Collections.nCopies;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+public class BackoffWithJitterTest {
+
+ static Logger log = LoggerFactory.getLogger(BackoffWithJitterTest.class);
+
+ interface PingPongService {
+
+ String call(String ping) throws PingPongServiceException;
+ }
+
+ class PingPongServiceException extends RuntimeException {
+
+ public PingPongServiceException(String reason) {
+ super(reason);
+ }
+ }
+
+ private PingPongService service;
+ private static final int NUM_TASKS = 8;
+
+ @Before
+ public void setUp() {
+ service = mock(PingPongService.class);
+ }
+
+ @Test
+ public void whenRetryExponentialBackoff_thenRetriedConfiguredNoOfTimes() {
+ IntervalFunction intervalFn = ofExponentialBackoff(INITIAL_INTERVAL, MULTIPLIER);
+ Function pingPongFn = getRetryablePingPongFn(intervalFn);
+
+ when(service.call(anyString())).thenThrow(PingPongServiceException.class);
+ try {
+ pingPongFn.apply("Hello");
+ } catch (PingPongServiceException e) {
+ verify(service, times(MAX_TRIES)).call(anyString());
+ }
+ }
+
+ private Function getRetryablePingPongFn(IntervalFunction intervalFn) {
+ RetryConfig retryConfig = RetryConfig.custom()
+ .maxAttempts(MAX_TRIES)
+ .intervalFunction(intervalFn)
+ .retryExceptions(PingPongServiceException.class)
+ .build();
+ Retry retry = Retry.of("pingpong", retryConfig);
+ return Retry.decorateFunction(retry, ping -> {
+ log.info("Invoked at {}", LocalDateTime.now());
+ return service.call(ping);
+ });
+ }
+
+ static class RetryProperties {
+ static final Long INITIAL_INTERVAL = 1000L;
+ static final Double MULTIPLIER = 2.0D;
+ static final Double RANDOMIZATION_FACTOR = 0.6D;
+ static final Integer MAX_TRIES = 4;
+ }
+}
diff --git a/patterns/pom.xml b/patterns/pom.xml
index 2be9d2519e..7f7368ca07 100644
--- a/patterns/pom.xml
+++ b/patterns/pom.xml
@@ -19,7 +19,8 @@
design-patterns
design-patterns-2
solid
- dip
+ dip
+ backoff-jitter