From 70d5788077acd578064ad497ff0ec42ce6d5ee0d Mon Sep 17 00:00:00 2001 From: KANISHKMAKKAR <102965884+KANISHKMAKKAR@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:54:04 +0530 Subject: [PATCH 1/2] fix: replace busy-wait in BallThread with wait/notify (#2977) --- .../java/com/iluwatar/twin/BallThread.java | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 7768d3ebbb99..b7b99b489ad0 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -36,6 +36,8 @@ public class BallThread extends Thread { @Setter private BallItem twin; + private final Object lock = new Object(); + private volatile boolean isSuspended; private volatile boolean isRunning = true; @@ -44,12 +46,18 @@ public class BallThread extends Thread { public void run() { while (isRunning) { - if (!isSuspended) { - twin.draw(); - twin.move(); - } try { - Thread.sleep(250); + synchronized (lock) { + // Wait to be notified instead of polling, so resuming takes effect immediately. + while (isSuspended && isRunning) { + lock.wait(); + } + } + if (isRunning) { + twin.draw(); + twin.move(); + Thread.sleep(250); + } } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -57,17 +65,25 @@ public void run() { } public void suspendMe() { - isSuspended = true; + synchronized (lock) { + isSuspended = true; + } LOGGER.info("Begin to suspend BallThread"); } public void resumeMe() { - isSuspended = false; + synchronized (lock) { + isSuspended = false; + lock.notifyAll(); + } LOGGER.info("Begin to resume BallThread"); } public void stopMe() { - this.isRunning = false; - this.isSuspended = true; + synchronized (lock) { + this.isRunning = false; + this.isSuspended = true; + lock.notifyAll(); + } } } From 7c10feca57f39c3493857bedb4c3437d72f62062 Mon Sep 17 00:00:00 2001 From: KANISHKMAKKAR <102965884+KANISHKMAKKAR@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:54:11 +0530 Subject: [PATCH 2/2] docs: update twin README to match BallThread fix --- twin/README.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/twin/README.md b/twin/README.md index f6fb6923c069..d07f49d20a01 100644 --- a/twin/README.md +++ b/twin/README.md @@ -88,17 +88,24 @@ public class BallItem extends GameItem { public class BallThread extends Thread { @Setter private BallItem twin; + private final Object lock = new Object(); private volatile boolean isSuspended; private volatile boolean isRunning = true; public void run() { while (isRunning) { - if (!isSuspended) { - twin.draw(); - twin.move(); - } try { - Thread.sleep(250); + synchronized (lock) { + // Wait to be notified instead of polling, so resuming takes effect immediately. + while (isSuspended && isRunning) { + lock.wait(); + } + } + if (isRunning) { + twin.draw(); + twin.move(); + Thread.sleep(250); + } } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -106,18 +113,26 @@ public class BallThread extends Thread { } public void suspendMe() { - isSuspended = true; + synchronized (lock) { + isSuspended = true; + } LOGGER.info("Begin to suspend BallThread"); } public void resumeMe() { - isSuspended = false; + synchronized (lock) { + isSuspended = false; + lock.notifyAll(); + } LOGGER.info("Begin to resume BallThread"); } public void stopMe() { - this.isRunning = false; - this.isSuspended = true; + synchronized (lock) { + this.isRunning = false; + this.isSuspended = true; + lock.notifyAll(); + } } } ```