Replacing Thread.sleep Loops with Proper Scheduling in Java
The article explains why using Thread.sleep in a loop causes busy‑waiting and performance problems, and demonstrates several Java scheduling alternatives—including Timer/TimerTask, ScheduledExecutorService, wait/notify, and CompletableFuture—to efficiently check a flag at fixed intervals.
Scenario : The original code checks a boolean flag every 3 seconds by calling Thread.sleep(3000) inside an infinite loop, which IDEs flag as busy‑waiting.
public static boolean FLAG = false;
public static void main(String[] args) throws InterruptedException {
while (true) {
Thread.sleep(3000); // check flag every 3 seconds
if (FLAG) {
doSomething();
break;
}
}
}
public static void doSomething() {
System.out.println('Hello World!!!');
}Reason analysis : Each call to Thread.sleep forces the operating system to suspend and later resume the thread, incurring context‑switch overhead, wasting CPU cycles, and potentially increasing latency, especially with short intervals.
Solution : Replace the busy‑wait with proper scheduling APIs. The following alternatives are presented.
Timer and TimerTask
These classic classes provide a simple way to schedule a task at a fixed rate. They are suitable for lightweight, single‑threaded timing needs.
import java.util.Timer;
import java.util.TimerTask;
public class Main {
public static boolean FLAG = false;
public static void main(String[] args) {
// create timer
Timer timer = new Timer();
// schedule task to run every 3 seconds
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (FLAG) {
doSomething();
timer.cancel(); // stop timer after work is done
}
}
}, 0, 3000);
}
public static void doSomething() {
System.out.println('Hello World!!!');
}
}Analysis : Timer.scheduleAtFixedRate() executes the task at a fixed frequency; timer.cancel() stops the timer when the condition is met.
ScheduledExecutorService
Introduced in Java 5, this modern API supports multithreaded scheduling, offering greater flexibility and robustness than Timer .
import java.util.concurrent.*;
public class Main {
public static boolean FLAG = false;
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
if (FLAG) {
doSomething();
scheduler.shutdown(); // shut down after work is done
}
}, 0, 3, TimeUnit.SECONDS); // run every 3 seconds
}
public static void doSomething() {
System.out.println('Hello World!!!');
}
}Analysis : The scheduler automatically manages task execution, avoiding explicit thread sleep and reducing busy‑waiting.
Object.wait() and Object.notify()
In a multithreaded environment, wait() and notify() can coordinate threads without constant polling.
public class Main {
private static final Object lock = new Object(); // synchronization lock
public static boolean FLAG = false;
public static void main(String[] args) throws InterruptedException {
// start a thread that checks the flag
Thread checkerThread = new Thread(() -> {
synchronized (lock) {
while (!FLAG) {
try {
lock.wait(3000); // wait up to 3 seconds or until notified
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
doSomething();
}
});
checkerThread.start();
// simulate other work, then set FLAG to true
Thread.sleep(5000); // wait 5 seconds
FLAG = true;
// wake up the waiting thread
synchronized (lock) {
lock.notify(); // notify waiting thread
}
}
public static void doSomething() {
System.out.println('Hello World!!!');
}
}Analysis : The waiting thread releases the lock and sleeps efficiently; notify() wakes it when the condition changes, eliminating busy‑waiting.
CompletableFuture
Java 8’s CompletableFuture provides powerful asynchronous programming, allowing non‑blocking checks of the flag.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class Main {
public static boolean FLAG = false;
public static void main(String[] args) {
// async task that checks FLAG every 3 seconds
CompletableFuture.runAsync(() -> {
while (!FLAG) {
try {
TimeUnit.SECONDS.sleep(3); // check every 3 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
doSomething();
});
// simulate other work, then set FLAG to true
try {
TimeUnit.SECONDS.sleep(5); // simulate delay
FLAG = true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void doSomething() {
System.out.println('Hello World!!!');
}
}Analysis : The asynchronous task runs in a separate thread, checking the flag without blocking the main thread, and executes the action once the flag becomes true.
All the presented approaches eliminate the performance drawbacks of busy‑waiting and provide clearer, more maintainable ways to perform periodic checks in Java applications.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.