From 3b6774829f808fdd0a7df5f62486a3cba2917b34 Mon Sep 17 00:00:00 2001 From: Anmol Singh Date: Thu, 6 Jun 2024 17:08:56 +0530 Subject: [PATCH] recommended changes commited --- multiton/bin/README.md | 162 +++++++++++++ multiton/bin/etc/multiton.png | Bin 0 -> 10637 bytes multiton/bin/etc/multiton.ucls | 44 ++++ multiton/bin/etc/multiton.urm.puml | 43 ++++ multiton/bin/pom.xml | 67 ++++++ producer-consumer/bin/README.md | 214 ++++++++++++++++++ .../queue/load/leveling/ServiceExecutor.java | 5 +- .../com/iluwatar/retry/BusinessException.java | 1 + .../main/java/com/iluwatar/retry/Retry.java | 1 - .../retry/RetryExponentialBackoff.java | 6 +- .../java/com/iluwatar/twin/BallThread.java | 1 + 11 files changed, 536 insertions(+), 8 deletions(-) create mode 100644 multiton/bin/README.md create mode 100644 multiton/bin/etc/multiton.png create mode 100644 multiton/bin/etc/multiton.ucls create mode 100644 multiton/bin/etc/multiton.urm.puml create mode 100644 multiton/bin/pom.xml create mode 100644 producer-consumer/bin/README.md diff --git a/multiton/bin/README.md b/multiton/bin/README.md new file mode 100644 index 000000000000..f3818bd0dace --- /dev/null +++ b/multiton/bin/README.md @@ -0,0 +1,162 @@ +--- +title: Multiton +category: Creational +language: en +tag: + - Decoupling + - Instantiation + - Object composition +--- + +## Also known as + +* Registry of Singletons + +## Intent + +The Multiton pattern is a variation of the Singleton design pattern that manages a map of named instances as key-value pairs. + +## Explanation + +Real-world example + +> A real-world example of the Multiton pattern is a printer management system in a large office. In this scenario, the office has several printers, each serving a different department. Instead of creating a new printer object every time a printing request is made, the system uses the Multiton pattern to ensure that each department has exactly one printer instance. When a printing request comes from a specific department, the system checks the registry of printer instances and retrieves the existing printer for that department. If no printer exists for that department, it creates one, registers it, and then returns it. This ensures efficient management of printer resources and prevents unnecessary creation of multiple printer instances for the same department. + +In plain words + +> Multiton pattern ensures there are a predefined amount of instances available globally. + +Wikipedia says + +> In software engineering, the multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map. + +**Programmatic Example** + +The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By definition, there's always nine of them. + +`Nazgul` is the multiton class. + +```java +public enum NazgulName { + + KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA +} + +public final class Nazgul { + + private static final Map nazguls; + + @Getter + private final NazgulName name; + + static { + nazguls = new ConcurrentHashMap<>(); + nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); + nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); + nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); + nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); + nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); + nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); + nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); + nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); + nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); + } + + private Nazgul(NazgulName name) { + this.name = name; + } + + public static Nazgul getInstance(NazgulName name) { + return nazguls.get(name); + } +} +``` + +And here's how we access the `Nazgul` instances. + +```java + public static void main(String[] args) { + // eagerly initialized multiton + LOGGER.info("Printing out eagerly initialized multiton contents"); + LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL)); + LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR)); + LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR)); + LOGGER.info("JI_INDUR={}", Nazgul.getInstance(NazgulName.JI_INDUR)); + LOGGER.info("AKHORAHIL={}", Nazgul.getInstance(NazgulName.AKHORAHIL)); + LOGGER.info("HOARMURATH={}", Nazgul.getInstance(NazgulName.HOARMURATH)); + LOGGER.info("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL)); + LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN)); + LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA)); + + // enum multiton + LOGGER.info("Printing out enum-based multiton contents"); + LOGGER.info("KHAMUL={}", NazgulEnum.KHAMUL); + LOGGER.info("MURAZOR={}", NazgulEnum.MURAZOR); + LOGGER.info("DWAR={}", NazgulEnum.DWAR); + LOGGER.info("JI_INDUR={}", NazgulEnum.JI_INDUR); + LOGGER.info("AKHORAHIL={}", NazgulEnum.AKHORAHIL); + LOGGER.info("HOARMURATH={}", NazgulEnum.HOARMURATH); + LOGGER.info("ADUNAPHEL={}", NazgulEnum.ADUNAPHEL); + LOGGER.info("REN={}", NazgulEnum.REN); + LOGGER.info("UVATHA={}", NazgulEnum.UVATHA); +} +``` + +Program output: + +``` +15:16:10.597 [main] INFO com.iluwatar.multiton.App -- Printing out eagerly initialized multiton contents +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- KHAMUL=com.iluwatar.multiton.Nazgul@4141d797 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- MURAZOR=com.iluwatar.multiton.Nazgul@38cccef +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- DWAR=com.iluwatar.multiton.Nazgul@5679c6c6 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- JI_INDUR=com.iluwatar.multiton.Nazgul@27ddd392 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- AKHORAHIL=com.iluwatar.multiton.Nazgul@19e1023e +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- HOARMURATH=com.iluwatar.multiton.Nazgul@7cef4e59 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- ADUNAPHEL=com.iluwatar.multiton.Nazgul@64b8f8f4 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- REN=com.iluwatar.multiton.Nazgul@2db0f6b2 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- UVATHA=com.iluwatar.multiton.Nazgul@3cd1f1c8 +15:16:10.600 [main] INFO com.iluwatar.multiton.App -- Printing out enum-based multiton contents +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- KHAMUL=KHAMUL +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- MURAZOR=MURAZOR +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- DWAR=DWAR +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- JI_INDUR=JI_INDUR +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- AKHORAHIL=AKHORAHIL +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- HOARMURATH=HOARMURATH +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- ADUNAPHEL=ADUNAPHEL +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- REN=REN +15:16:10.601 [main] INFO com.iluwatar.multiton.App -- UVATHA=UVATHA +``` + +## Applicability + +Use the Multiton pattern when + +* A class must have named instances, but only one instance for each unique key. +* Global access to these instances is necessary without requiring global variables. +* You want to manage shared resources categorized by key. + +## Known Uses + +* Managing database connections in different contexts. +* Configuration settings for different environments in an application. + +## Consequences + +Benefits: + +* Ensures controlled access to instances based on key. +* Reduces global state usage by encapsulating instance management within the pattern. + +Trade-offs: + +* Increased memory usage if not managed properly due to multiple instances. +* Potential issues with concurrency if not implemented with thread safety in mind. + +## Related Patterns + +* [Singleton](https://java-design-patterns.com/patterns/singleton/): Multiton can be seen as an extension of the Singleton pattern where Singleton allows only one instance of a class, Multiton allows one instance per key. +* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Multiton uses a method to create or retrieve instances, similar to how a Factory Method controls object creation. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) diff --git a/multiton/bin/etc/multiton.png b/multiton/bin/etc/multiton.png new file mode 100644 index 0000000000000000000000000000000000000000..91106463d2b23ab568fe2634c9742a378e16942f GIT binary patch literal 10637 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 QKeN0gC#5Kf5ZCki4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/multiton/bin/etc/multiton.urm.puml b/multiton/bin/etc/multiton.urm.puml new file mode 100644 index 000000000000..5380d36c3146 --- /dev/null +++ b/multiton/bin/etc/multiton.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.multiton { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Nazgul { + - name : NazgulName + - nazguls : Map {static} + - Nazgul(name : NazgulName) + + getInstance(name : NazgulName) : Nazgul {static} + + getName() : NazgulName + } + enum NazgulEnum { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulEnum {static} + + values() : NazgulEnum[] {static} + } + enum NazgulName { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulName {static} + + values() : NazgulName[] {static} + } +} +Nazgul --> "-name" NazgulName +@enduml \ No newline at end of file diff --git a/multiton/bin/pom.xml b/multiton/bin/pom.xml new file mode 100644 index 000000000000..6d2430560a5c --- /dev/null +++ b/multiton/bin/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + multiton + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.multiton.App + + + + + + + + + diff --git a/producer-consumer/bin/README.md b/producer-consumer/bin/README.md new file mode 100644 index 000000000000..c6821766c3af --- /dev/null +++ b/producer-consumer/bin/README.md @@ -0,0 +1,214 @@ +--- +title: Producer-Consumer +category: Concurrency +language: en +tag: + - Asynchronous + - Buffering + - Decoupling + - Messaging + - Synchronization + - Thread management +--- + +## Also known as + +* Bounded Buffer +* Consumer-Producer + +## Intent + +The Producer-Consumer design pattern is used to decouple the tasks of producing and consuming data, enabling a producer to generate data and a consumer to process that data concurrently without direct dependency on each other. + +## Explanation + +Real-world example + +> A real-world example of the Producer-Consumer design pattern can be found in a factory assembly line. Imagine a car manufacturing plant where different stages of production occur. The "producer" could be the station that assembles car engines, while the "consumer" could be the station that installs the engines into car bodies. The engines are placed onto a conveyor belt (acting as a buffer) once they are assembled. The installation station takes engines off the conveyor belt to install them into cars. This allows the engine assembly and engine installation processes to operate independently, with the conveyor belt managing the synchronization between these two stages. If the assembly station produces engines faster than the installation station can install them, the excess engines are temporarily stored on the conveyor belt. Conversely, if the installation station needs engines but the assembly station is temporarily halted, it can still work on the engines available on the belt. + +In plain words + +> It provides a way to share data between multiple loops running at different rates. + +Wikipedia says + +> Dijkstra wrote about the case: "We consider two processes, which are called the 'producer' and the 'consumer' respectively. The producer is a cyclic process that produces a certain portion of information, that has to be processed by the consumer. The consumer is also a cyclic process that needs to process the next portion of information, as has been produced by the producer. We assume the two processes to be connected for this purpose via a buffer with unbounded capacity." + +**Programmatic Example** + +Consider a manufacturing process of item, the producer will need to pause the production when manufacturing pipeline is full and the consumer will need to pause the consumption of item when the manufacturing pipeline is empty. We can separate the process of production and consumption which work together and pause at separate times. + +We have a simple `Item` record. They are stored in `ItemQueue`. + +```java +public record Item(String producer, int id) {} +``` + +```java +public class ItemQueue { + + private final BlockingQueue queue; + + public ItemQueue() { + queue = new LinkedBlockingQueue<>(5); + } + + public void put(Item item) throws InterruptedException { + queue.put(item); + } + + public Item take() throws InterruptedException { + return queue.take(); + } +} +``` + +`Producer` produces items to the `ItemQueue`. + +```java +public class Producer { + + private static final SecureRandom RANDOM = new SecureRandom(); + private final ItemQueue queue; + private final String name; + private int itemId; + + public Producer(String name, ItemQueue queue) { + this.name = name; + this.queue = queue; + } + + public void produce() throws InterruptedException { + + var item = new Item(name, itemId++); + queue.put(item); + Thread.sleep(RANDOM.nextInt(2000)); + } +} +``` + +Then, we have the `Consumer` class that takes items from the `ItemQueue`. + +```java +@Slf4j +public class Consumer { + + private final ItemQueue queue; + private final String name; + + public Consumer(String name, ItemQueue queue) { + this.name = name; + this.queue = queue; + } + + public void consume() throws InterruptedException { + var item = queue.take(); + LOGGER.info("Consumer [{}] consume item [{}] produced by [{}]", name, + item.getId(), item.getProducer()); + + } +} +``` + +Now, during the manufacturing pipeline, we can instantiate objects from both the `Producer` and `Consumer` classes as they produce and consume items from the queue. + +```java + public static void main(String[] args) { + + var queue = new ItemQueue(); + + var executorService = Executors.newFixedThreadPool(5); + for (var i = 0; i < 2; i++) { + + final var producer = new Producer("Producer_" + i, queue); + executorService.submit(() -> { + while (true) { + producer.produce(); + } + }); + } + + for (var i = 0; i < 3; i++) { + final var consumer = new Consumer("Consumer_" + i, queue); + executorService.submit(() -> { + while (true) { + consumer.consume(); + } + }); + } + + executorService.shutdown(); + try { + executorService.awaitTermination(10, TimeUnit.SECONDS); + executorService.shutdownNow(); + } catch (InterruptedException e) { + LOGGER.error("Error waiting for ExecutorService shutdown"); + } +} +``` + +Program output: + +``` +08:10:08.008 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [0] produced by [Producer_1] +08:10:08.008 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [0] produced by [Producer_0] +08:10:08.517 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [1] produced by [Producer_0] +08:10:08.952 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [1] produced by [Producer_1] +08:10:09.208 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [2] produced by [Producer_0] +08:10:09.354 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [2] produced by [Producer_1] +08:10:10.214 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [3] produced by [Producer_1] +08:10:10.585 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [3] produced by [Producer_0] +08:10:11.530 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [4] produced by [Producer_1] +08:10:11.682 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [4] produced by [Producer_0] +08:10:11.781 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [5] produced by [Producer_0] +08:10:12.209 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [5] produced by [Producer_1] +08:10:13.045 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [6] produced by [Producer_0] +08:10:13.861 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [6] produced by [Producer_1] +08:10:14.739 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [7] produced by [Producer_1] +08:10:14.842 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [7] produced by [Producer_0] +08:10:15.975 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [8] produced by [Producer_0] +08:10:16.378 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [8] produced by [Producer_1] +08:10:16.967 [pool-1-thread-3] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_0] consume item [9] produced by [Producer_0] +08:10:17.417 [pool-1-thread-4] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_1] consume item [9] produced by [Producer_1] +08:10:17.483 [pool-1-thread-5] INFO com.iluwatar.producer.consumer.Consumer -- Consumer [Consumer_2] consume item [10] produced by [Producer_1] +``` + +## Class diagram + +![Producer-Consumer](./etc/producer-consumer.png "Producer-Consumer") + +## Applicability + +* When you need to manage a buffer or queue where producers add data and consumers take data, often in a multithreaded environment. +* When decoupling the production and consumption of data is beneficial for the application's design, performance, or maintainability. +* Suitable for scenarios requiring synchronized access to a shared resource or data structure. + +## Known Uses + +* Thread pools where worker threads act as consumers processing tasks produced by another thread. +* Logging frameworks where log messages are produced by various parts of an application and consumed by a logging service. +* Message queues in distributed systems for asynchronous communication between services. + +## Consequences + +Benefits: + +* Decoupling: Producers and consumers can operate independently, simplifying the system design. +* Improved Performance: Allows multiple producer and consumer threads to work concurrently, improving throughput. +* Flexibility: Easily extendable to add more producers or consumers without significant changes to the existing system. + +Trade-offs: + +* Complexity: Requires careful handling of synchronization and potential deadlocks. +* Resource Management: Properly managing the buffer size to avoid overflow or underflow conditions. + +## Related Patterns + +* [Observer](https://java-design-patterns.com/patterns/observer/): While both deal with notifying or handling events, the Observer pattern is more about event subscription and notification, whereas Producer-Consumer focuses on decoupled data production and consumption. +* [Thread Pool](https://java-design-patterns.com/patterns/thread-pool/): Uses a similar decoupling approach where tasks are produced and consumed by a pool of worker threads. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +* [Effective Java](https://amzn.to/4cGk2Jz) +* [Java Concurrency in Practice](https://amzn.to/4aRMruW) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java index f5d48dd92170..b77bc29d8888 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java @@ -25,7 +25,6 @@ package com.iluwatar.queue.load.leveling; import lombok.extern.slf4j.Slf4j; - import java.util.concurrent.BlockingQueue; /** @@ -34,7 +33,6 @@ * process them. */ @Slf4j - public class ServiceExecutor implements Runnable { private final BlockingQueue msgQueue; @@ -50,7 +48,6 @@ public void run() { try { while (true) { Message msg = msgQueue.take(); // This will block until a message is available - LOGGER.info(msg + " is served."); } } catch (InterruptedException e) { @@ -58,4 +55,4 @@ public void run() { Thread.currentThread().interrupt(); } } -} +} \ No newline at end of file diff --git a/retry/src/main/java/com/iluwatar/retry/BusinessException.java b/retry/src/main/java/com/iluwatar/retry/BusinessException.java index b6b385132fe1..fe79df820b9e 100644 --- a/retry/src/main/java/com/iluwatar/retry/BusinessException.java +++ b/retry/src/main/java/com/iluwatar/retry/BusinessException.java @@ -57,3 +57,4 @@ public BusinessException(Throwable cause) { super(cause); } } + \ No newline at end of file diff --git a/retry/src/main/java/com/iluwatar/retry/Retry.java b/retry/src/main/java/com/iluwatar/retry/Retry.java index 40c0591302d0..480a558fc804 100644 --- a/retry/src/main/java/com/iluwatar/retry/Retry.java +++ b/retry/src/main/java/com/iluwatar/retry/Retry.java @@ -120,7 +120,6 @@ private void retryAttempt(CompletableFuture future, int attempt) { } } }); - if (attempt < maxAttempts) { try { Thread.sleep(delay); diff --git a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java index fa8d15067ba0..eb9cfa6faa2a 100644 --- a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java +++ b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java @@ -49,7 +49,7 @@ public final class RetryExponentialBackoff implements BusinessOperation { private final AtomicInteger attempts; /** - * Constructor. + * Constructor * * @param op the {@link BusinessOperation} to retry * @param maxAttempts number of times to retry @@ -70,7 +70,7 @@ public RetryExponentialBackoff(BusinessOperation op, int maxAttempts, long ma } /** - * The errors encountered while retrying, in the order of occurrence. + * The errors encountered while retrying, in the order of occurence. * * @return the errors encountered while retrying */ @@ -144,4 +144,4 @@ private long calculateDelay(int attempt) { long testDelay = (long) Math.pow(2, attempt) * 1000 + RANDOM.nextInt(1000); return Math.min(testDelay, maxDelay); } -} +} \ No newline at end of file diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index d96bf390d734..0009428f281f 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -35,6 +35,7 @@ import java.util.concurrent.CountDownLatch; @Slf4j + public class BallThread extends Thread { @Setter