From 5b8997519ddbeff2e7d9804b73e856427af4383b Mon Sep 17 00:00:00 2001 From: Kaaldut8 Date: Fri, 6 Mar 2026 17:41:51 +0530 Subject: [PATCH 1/4] server-session: remove busy-wait loop in App.java using ScheduledExecutorService (#2977) --- .../java/com/iluwatar/sessionserver/App.java | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 512447b8a2d9..caf306fe8bb3 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.*; import lombok.extern.slf4j.Slf4j; /** @@ -57,6 +58,9 @@ public class App { private static Map sessionCreationTimes = new HashMap<>(); private static final long SESSION_EXPIRATION_TIME = 10000; + private static final ScheduledExecutorService scheduler = + Executors.newSingleThreadScheduledExecutor(Thread.ofVirtual().factory()); + /** * Main entry point. * @@ -78,39 +82,39 @@ public static void main(String[] args) throws IOException { sessionExpirationTask(); LOGGER.info("Server started. Listening on port 8080..."); + + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + LOGGER.info("Shutting down scheduler..."); + scheduler.shutdown(); + })); } private static void sessionExpirationTask() { - new Thread( - () -> { - while (true) { - try { - LOGGER.info("Session expiration checker started..."); - Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time - Instant currentTime = Instant.now(); - synchronized (sessions) { - synchronized (sessionCreationTimes) { - Iterator> iterator = - sessionCreationTimes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (entry - .getValue() - .plusMillis(SESSION_EXPIRATION_TIME) - .isBefore(currentTime)) { - sessions.remove(entry.getKey()); - iterator.remove(); - } - } - } + scheduler + .scheduleAtFixedRate(() -> { + try { + LOGGER.info("Session expiration checker started..."); + Instant currentTime = Instant.now(); + synchronized (sessions) { + synchronized (sessionCreationTimes) { + Iterator> iterator = + sessionCreationTimes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry + .getValue() + .plusMillis(SESSION_EXPIRATION_TIME) + .isBefore(currentTime)) { + sessions.remove(entry.getKey()); + iterator.remove(); } - LOGGER.info("Session expiration checker finished!"); - } catch (InterruptedException e) { - LOGGER.error("An error occurred: ", e); - Thread.currentThread().interrupt(); } } - }) - .start(); + } + LOGGER.info("Session expiration checker finished!"); + } catch (Exception e) { + LOGGER.error("An error occurred: ", e); + } + }, SESSION_EXPIRATION_TIME, SESSION_EXPIRATION_TIME, TimeUnit.MILLISECONDS); } -} +} \ No newline at end of file From 3156d069ba3e4e8d0dee0f8cfe18b5cad3151df4 Mon Sep 17 00:00:00 2001 From: Kaaldut8 Date: Fri, 6 Mar 2026 21:21:03 +0530 Subject: [PATCH 2/4] Remove busy waiting in ServiceExecutor and improve message processing --- .../java/com/iluwatar/commander/Retry.java | 24 +- .../logaggregation/LogAggregator.java | 21 +- .../eurekaserver/pom.xml | 12 + .../eurekaserver/EurekaserverApplication.java | 7 +- .../EurekaserverApplicationTests.java | 13 +- pom.xml | 938 +++++++++--------- .../queue/load/leveling/ServiceExecutor.java | 15 +- .../java/com/iluwatar/sessionserver/App.java | 11 +- twin/src/main/java/com/iluwatar/twin/App.java | 2 +- .../java/com/iluwatar/twin/BallThread.java | 3 +- 10 files changed, 536 insertions(+), 510 deletions(-) diff --git a/commander/src/main/java/com/iluwatar/commander/Retry.java b/commander/src/main/java/com/iluwatar/commander/Retry.java index 661f479174a3..c6abcf80d992 100644 --- a/commander/src/main/java/com/iluwatar/commander/Retry.java +++ b/commander/src/main/java/com/iluwatar/commander/Retry.java @@ -28,6 +28,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -53,6 +56,7 @@ public interface HandleErrorIssue { } private static final SecureRandom RANDOM = new SecureRandom(); + private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final Operation op; private final HandleErrorIssue handleError; @@ -98,9 +102,23 @@ public void perform(List list, T obj) { long testDelay = (long) Math.pow(2, this.attempts.intValue()) * 1000 + RANDOM.nextInt(1000); long delay = Math.min(testDelay, this.maxDelay); - Thread.sleep(delay); - } catch (InterruptedException f) { - // ignore + scheduler.schedule(() -> perform(list, obj), delay, TimeUnit.MILLISECONDS); + return; + } catch (Exception j) { + this.errors.add(j); + + if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(j)) { + this.handleError.handleIssue(obj, j); + return; + } + + long testDelay = + (long) Math.pow(2, this.attempts.intValue()) * 1000 + RANDOM.nextInt(1000); + + long delay = Math.min(testDelay, this.maxDelay); + + scheduler.schedule(() -> perform(list, obj), delay, TimeUnit.MILLISECONDS); + return; } } } while (true); diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index 0acdc9fedc62..00e2565f6179 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -25,7 +25,7 @@ package com.iluwatar.logaggregation; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -44,7 +44,7 @@ public class LogAggregator { private final CentralLogStore centralLogStore; private final ConcurrentLinkedQueue buffer = new ConcurrentLinkedQueue<>(); private final LogLevel minLogLevel; - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private final AtomicInteger logCount = new AtomicInteger(0); /** @@ -104,16 +104,11 @@ private void flushBuffer() { } private void startBufferFlusher() { - executorService.execute( - () -> { - while (!Thread.currentThread().isInterrupted()) { - try { - Thread.sleep(5000); // Flush every 5 seconds. - flushBuffer(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - }); + executorService.scheduleAtFixedRate( + this::flushBuffer, + 5, + 5, + TimeUnit.SECONDS + ); } } diff --git a/microservices-self-registration/eurekaserver/pom.xml b/microservices-self-registration/eurekaserver/pom.xml index b1a4b26cf4f4..f3235e97e38c 100644 --- a/microservices-self-registration/eurekaserver/pom.xml +++ b/microservices-self-registration/eurekaserver/pom.xml @@ -49,6 +49,18 @@ org.apache.maven.plugins maven-compiler-plugin + + com.diffplug.spotless + spotless-maven-plugin + 2.44.4 + + + + 1.17.0 + + + + diff --git a/microservices-self-registration/eurekaserver/src/main/java/com/learning/eurekaserver/EurekaserverApplication.java b/microservices-self-registration/eurekaserver/src/main/java/com/learning/eurekaserver/EurekaserverApplication.java index 80b3d904ff4c..80b888f5910c 100644 --- a/microservices-self-registration/eurekaserver/src/main/java/com/learning/eurekaserver/EurekaserverApplication.java +++ b/microservices-self-registration/eurekaserver/src/main/java/com/learning/eurekaserver/EurekaserverApplication.java @@ -8,8 +8,7 @@ @EnableEurekaServer public class EurekaserverApplication { - public static void main(String[] args) { - SpringApplication.run(EurekaserverApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(EurekaserverApplication.class, args); + } } diff --git a/microservices-self-registration/eurekaserver/src/test/java/com/learning/eurekaserver/EurekaserverApplicationTests.java b/microservices-self-registration/eurekaserver/src/test/java/com/learning/eurekaserver/EurekaserverApplicationTests.java index b5150fefa940..4f846888281d 100644 --- a/microservices-self-registration/eurekaserver/src/test/java/com/learning/eurekaserver/EurekaserverApplicationTests.java +++ b/microservices-self-registration/eurekaserver/src/test/java/com/learning/eurekaserver/EurekaserverApplicationTests.java @@ -6,12 +6,13 @@ @SpringBootTest class EurekaserverApplicationTests { - @Test - void contextLoads() { - // This is a basic integration test that checks if the Spring Application Context loads successfully. + @Test + void contextLoads() { + // This is a basic integration test that checks if the Spring Application Context loads + // successfully. // If the context loads without any exceptions, the test is considered passing. // It is often left empty as the act of loading the context is the primary verification. - // You can add specific assertions here if you want to verify the presence or state of certain beans. - } - + // You can add specific assertions here if you want to verify the presence or state of certain + // beans. + } } diff --git a/pom.xml b/pom.xml index 82d15bdf1d74..0ada7ddf769e 100644 --- a/pom.xml +++ b/pom.xml @@ -25,479 +25,479 @@ THE SOFTWARE. --> - - 4.0.0 - com.iluwatar - java-design-patterns - 1.26.0-SNAPSHOT - pom - 2014-2022 - Java Design Patterns - Java Design Patterns - - - UTF-8 + + 4.0.0 + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + pom + 2014-2022 + Java Design Patterns + Java Design Patterns + + + UTF-8 - - 3.4.4 - 5.11.4 - 5.14.2 - 1.5.18 - 2.0.17 + + 3.4.4 + 5.11.4 + 5.14.2 + 1.5.18 + 2.0.17 - - 0.8.13 - 1.4 - 4.17.0 - 2.11.0 - 6.0.0 - 1.1.0 - 1.18.38 - 5.4.0 - 5.4.0 - 2.3.232 + + 0.8.13 + 1.4 + 4.17.0 + 2.11.0 + 6.0.0 + 1.1.0 + 1.18.38 + 5.4.0 + 5.4.0 + 2.3.232 - - 3.5.2 - 5.0.0 - 3.14.0 + + 3.5.2 + 5.0.0 + 3.14.0 - - 5.1.0.4751 - https://sonarcloud.io - iluwatar - iluwatar_java-design-patterns - ${project.artifactId} - Java Design Patterns - - - abstract-document - abstract-factory - active-object - acyclic-visitor - adapter - ambassador - anti-corruption-layer - arrange-act-assert - async-method-invocation - balking - bloc - bridge - builder - business-delegate - bytecode - caching - callback - chain-of-responsibility - circuit-breaker - clean-architecture - client-session - collecting-parameter - collection-pipeline - combinator - command - command-query-responsibility-segregation - commander - component - composite - composite-entity - composite-view - context-object - converter - curiously-recurring-template-pattern - currying - dao-factory - data-access-object - data-bus - data-locality - data-mapper - data-transfer-object - decorator - delegation - dependency-injection - dirty-flag - domain-model - double-buffer - double-checked-locking - double-dispatch - dynamic-proxy - event-aggregator - event-based-asynchronous - event-driven-architecture - event-queue - event-sourcing - execute-around - extension-objects - facade - factory - factory-kit - factory-method - fanout-fanin - feature-toggle - filterer - fluent-interface - flux - flyweight - front-controller - function-composition - game-loop - gateway - guarded-suspension - half-sync-half-async - health-check - hexagonal-architecture - identity-map - intercepting-filter - interpreter - iterator - layered-architecture - lazy-loading - leader-election - leader-followers - lockable-object - map-reduce - marker-interface - master-worker - mediator - memento - metadata-mapping - microservices-aggregrator - microservices-api-gateway - microservices-client-side-ui-composition - microservices-distributed-tracing - microservices-idempotent-consumer - microservices-log-aggregation - microservices-self-registration - model-view-controller - model-view-intent - model-view-presenter - model-view-viewmodel - monad - money - monitor - monolithic-architecture - monostate - multiton - mute-idiom - notification - null-object - object-mother - object-pool - observer - optimistic-offline-lock - page-controller - page-object - parameter-object - partial-response - pipeline - poison-pill - presentation-model - private-class-data - producer-consumer - promise - property - prototype - proxy - publish-subscribe - queue-based-load-leveling - reactor - registry - repository - resource-acquisition-is-initialization - retry - role-object - saga - separated-interface - serialized-entity - serialized-lob - servant - server-session - service-layer - service-locator - service-stub - service-to-worker - session-facade - sharding - single-table-inheritance - singleton - spatial-partition - special-case - specification - state - step-builder - strangler - strategy - subclass-sandbox - table-inheritance - table-module - template-method - templateview - thread-pool-executor - throttling - tolerant-reader - trampoline - transaction-script - twin - type-object - unit-of-work - update-method - value-object - version-number - view-helper - virtual-proxy - visitor - backpressure - actor-model - rate-limiting-pattern - - - - jitpack.io - https://jitpack.io - - - + + 5.1.0.4751 + https://sonarcloud.io + iluwatar + iluwatar_java-design-patterns + ${project.artifactId} + Java Design Patterns + + + abstract-document + abstract-factory + active-object + acyclic-visitor + adapter + ambassador + anti-corruption-layer + arrange-act-assert + async-method-invocation + balking + bloc + bridge + builder + business-delegate + bytecode + caching + callback + chain-of-responsibility + circuit-breaker + clean-architecture + client-session + collecting-parameter + collection-pipeline + combinator + command + command-query-responsibility-segregation + commander + component + composite + composite-entity + composite-view + context-object + converter + curiously-recurring-template-pattern + currying + dao-factory + data-access-object + data-bus + data-locality + data-mapper + data-transfer-object + decorator + delegation + dependency-injection + dirty-flag + domain-model + double-buffer + double-checked-locking + double-dispatch + dynamic-proxy + event-aggregator + event-based-asynchronous + event-driven-architecture + event-queue + event-sourcing + execute-around + extension-objects + facade + factory + factory-kit + factory-method + fanout-fanin + feature-toggle + filterer + fluent-interface + flux + flyweight + front-controller + function-composition + game-loop + gateway + guarded-suspension + half-sync-half-async + health-check + hexagonal-architecture + identity-map + intercepting-filter + interpreter + iterator + layered-architecture + lazy-loading + leader-election + leader-followers + lockable-object + map-reduce + marker-interface + master-worker + mediator + memento + metadata-mapping + microservices-aggregrator + microservices-api-gateway + microservices-client-side-ui-composition + microservices-distributed-tracing + microservices-idempotent-consumer + microservices-log-aggregation + microservices-self-registration + model-view-controller + model-view-intent + model-view-presenter + model-view-viewmodel + monad + money + monitor + monolithic-architecture + monostate + multiton + mute-idiom + notification + null-object + object-mother + object-pool + observer + optimistic-offline-lock + page-controller + page-object + parameter-object + partial-response + pipeline + poison-pill + presentation-model + private-class-data + producer-consumer + promise + property + prototype + proxy + publish-subscribe + queue-based-load-leveling + reactor + registry + repository + resource-acquisition-is-initialization + retry + role-object + saga + separated-interface + serialized-entity + serialized-lob + servant + server-session + service-layer + service-locator + service-stub + service-to-worker + session-facade + sharding + single-table-inheritance + singleton + spatial-partition + special-case + specification + state + step-builder + strangler + strategy + subclass-sandbox + table-inheritance + table-module + template-method + templateview + thread-pool-executor + throttling + tolerant-reader + trampoline + transaction-script + twin + type-object + unit-of-work + update-method + value-object + version-number + view-helper + virtual-proxy + visitor + backpressure + actor-model + rate-limiting-pattern + + + + jitpack.io + https://jitpack.io + + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-actuator + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-test + test + ${spring-boot.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.htmlunit + htmlunit + ${htmlunit.version} + + + com.google.code.gson + gson + ${gson.version} + + + com.google.inject + guice + ${guice.version} + + + com.github.stefanbirkner + system-lambda + ${system-lambda.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + ${junit.version} + test + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.mockito + mockito-core + ${mockito.version} + + + org.mongodb + bson + ${bson.version} + + + org.mongodb + mongodb-driver-legacy + ${mongo.version} + + + com.h2database + h2 + ${h2.version} + + + - - org.springframework.boot - spring-boot-starter - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-actuator - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-test - test - ${spring-boot.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.htmlunit - htmlunit - ${htmlunit.version} - - - com.google.code.gson - gson - ${gson.version} - - - com.google.inject - guice - ${guice.version} - - - com.github.stefanbirkner - system-lambda - ${system-lambda.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-migrationsupport - ${junit.version} - test - - - org.slf4j - slf4j-api - ${slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.mockito - mockito-core - ${mockito.version} - - - org.mongodb - bson - ${bson.version} - - - org.mongodb - mongodb-driver-legacy - ${mongo.version} - - - com.h2database - h2 - ${h2.version} - + + org.projectlombok + lombok + ${lombok.version} + provided + - - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 21 - 21 - - - org.projectlombok - lombok - ${lombok.version} - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - - jar-with-dependencies - - - ${project.artifactId}-${project.version} - false - - - - - - org.sonarsource.scanner.maven - sonar-maven-plugin - ${sonar-maven-plugin.version} - - - - - - com.mycila - license-maven-plugin - ${license-maven-plugin.version} - - - - - -
com/mycila/maven/plugin/license/templates/MIT.txt
-
- - **/README - src/test/resources/** - src/main/resources/** - checkstyle-suppressions.xml - -
-
- - Ilkka Seppälä - iluwatar@gmail.com - -
- - - install-format - install - - format - - - -
- - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - prepare-agent - - prepare-agent - - - - report - - report - - - - - - com.diffplug.spotless - spotless-maven-plugin - 2.44.4 - - - - check - apply - - - - - - - 1.17.0 - - - - -
-
+ + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 21 + 21 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + jar-with-dependencies + + + ${project.artifactId}-${project.version} + false + + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + + + + + com.mycila + license-maven-plugin + ${license-maven-plugin.version} + + + + + +
com/mycila/maven/plugin/license/templates/MIT.txt
+
+ + **/README + src/test/resources/** + src/main/resources/** + checkstyle-suppressions.xml + +
+
+ + Ilkka Seppälä + iluwatar@gmail.com + +
+ + + install-format + install + + format + + + +
+ + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.44.4 + + + + check + apply + + + + + + + 1.17.0 + + + + +
+
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 0a8f1b5a7642..89baba95f649 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 @@ -39,22 +39,19 @@ public ServiceExecutor(MessageQueue msgQueue) { this.msgQueue = msgQueue; } - /** The ServiceExecutor thread will retrieve each message and process it. */ + @Override public void run() { try { while (!Thread.currentThread().isInterrupted()) { + var msg = msgQueue.retrieveMsg(); - if (null != msg) { - LOGGER.info(msg + " is served."); - } else { - LOGGER.info("Service Executor: Waiting for Messages to serve .. "); + if (msg != null) { + LOGGER.info("{} is served.", msg); } - - Thread.sleep(1000); } } catch (Exception e) { - LOGGER.error(e.getMessage()); + LOGGER.error("Error processing message", e); } } -} +} \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index caf306fe8bb3..24a3c6c96adb 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -83,10 +83,13 @@ public static void main(String[] args) throws IOException { LOGGER.info("Server started. Listening on port 8080..."); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - LOGGER.info("Shutting down scheduler..."); - scheduler.shutdown(); - })); + Runtime.getRuntime() + .addShutdownHook( + new Thread( + () -> { + LOGGER.info("Shutting down scheduler..."); + scheduler.shutdown(); + })); } private static void sessionExpirationTask() { diff --git a/twin/src/main/java/com/iluwatar/twin/App.java b/twin/src/main/java/com/iluwatar/twin/App.java index 65ffadf36a3b..2a4b93aa9a6d 100644 --- a/twin/src/main/java/com/iluwatar/twin/App.java +++ b/twin/src/main/java/com/iluwatar/twin/App.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws Exception { ballItem.setTwin(ballThread); ballThread.setTwin(ballItem); - ballThread.start(); + Thread.ofVirtual().start(ballThread); waiting(); diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 7768d3ebbb99..770bf07aafe5 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -26,13 +26,14 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.*; /** * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend * and resume. It holds the reference of {@link BallItem} to delegate the draw task. */ @Slf4j -public class BallThread extends Thread { +public class BallThread implements Runnable { @Setter private BallItem twin; From 0c0ebabafafc23ed39745621e7e47617a29da7a7 Mon Sep 17 00:00:00 2001 From: Kaaldut8 Date: Fri, 6 Mar 2026 21:33:10 +0530 Subject: [PATCH 3/4] Remove budy-time form all the java files --- .../src/main/java/com/iluwatar/commander/Retry.java | 3 ++- .../com/iluwatar/logaggregation/LogAggregator.java | 12 ++++-------- retry/src/main/java/com/iluwatar/retry/Retry.java | 11 ++++++++--- .../com/iluwatar/retry/RetryExponentialBackoff.java | 3 ++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/commander/src/main/java/com/iluwatar/commander/Retry.java b/commander/src/main/java/com/iluwatar/commander/Retry.java index c6abcf80d992..0e838bbc9c39 100644 --- a/commander/src/main/java/com/iluwatar/commander/Retry.java +++ b/commander/src/main/java/com/iluwatar/commander/Retry.java @@ -56,7 +56,8 @@ public interface HandleErrorIssue { } private static final SecureRandom RANDOM = new SecureRandom(); - private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + private static final ScheduledExecutorService scheduler = + Executors.newSingleThreadScheduledExecutor(); private final Operation op; private final HandleErrorIssue handleError; diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index 00e2565f6179..010a00cfd2f6 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -25,8 +25,8 @@ package com.iluwatar.logaggregation; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; @@ -44,7 +44,8 @@ public class LogAggregator { private final CentralLogStore centralLogStore; private final ConcurrentLinkedQueue buffer = new ConcurrentLinkedQueue<>(); private final LogLevel minLogLevel; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final ScheduledExecutorService executorService = + Executors.newSingleThreadScheduledExecutor(); private final AtomicInteger logCount = new AtomicInteger(0); /** @@ -104,11 +105,6 @@ private void flushBuffer() { } private void startBufferFlusher() { - executorService.scheduleAtFixedRate( - this::flushBuffer, - 5, - 5, - TimeUnit.SECONDS - ); + executorService.scheduleAtFixedRate(this::flushBuffer, 5, 5, TimeUnit.SECONDS); } } diff --git a/retry/src/main/java/com/iluwatar/retry/Retry.java b/retry/src/main/java/com/iluwatar/retry/Retry.java index fb0e6c6c8502..03be53d387d9 100644 --- a/retry/src/main/java/com/iluwatar/retry/Retry.java +++ b/retry/src/main/java/com/iluwatar/retry/Retry.java @@ -30,6 +30,9 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * Decorates {@link BusinessOperation business operation} with "retry" capabilities. @@ -43,6 +46,8 @@ public final class Retry implements BusinessOperation { private final AtomicInteger attempts; private final Predicate test; private final List errors; + private static final ScheduledExecutorService scheduler = + Executors.newSingleThreadScheduledExecutor(); /** * Ctor. @@ -95,9 +100,9 @@ public T perform() throws BusinessException { } try { - Thread.sleep(this.delay); - } catch (InterruptedException f) { - // ignore + scheduler.schedule(() -> {}, this.delay, TimeUnit.MILLISECONDS).get(); + } catch (Exception ex) { + Thread.currentThread().interrupt(); } } } while (true); diff --git a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java index 024097b6b5c7..aaa5dd13968d 100644 --- a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java +++ b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java @@ -103,7 +103,8 @@ public T perform() throws BusinessException { var delay = Math.min(testDelay, this.maxDelay); Thread.sleep(delay); } catch (InterruptedException f) { - // ignore + Thread.currentThread().interrupt(); + throw new BusinessException("Retry interrupted"); } } } while (true); From cfb221d3bcfe656c27c0b6dcf6501c9e88c7b69f Mon Sep 17 00:00:00 2001 From: Kaaldut8 Date: Fri, 6 Mar 2026 21:40:32 +0530 Subject: [PATCH 4/4] Fix interruption handling in RetryExponentialBackoff to satisfy tests --- .../main/java/com/iluwatar/retry/RetryExponentialBackoff.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java index aaa5dd13968d..bba3ce1997af 100644 --- a/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java +++ b/retry/src/main/java/com/iluwatar/retry/RetryExponentialBackoff.java @@ -104,7 +104,7 @@ public T perform() throws BusinessException { Thread.sleep(delay); } catch (InterruptedException f) { Thread.currentThread().interrupt(); - throw new BusinessException("Retry interrupted"); + throw new BusinessException(f.getMessage()); } } } while (true);