I have created a simple HealthCheck service. I have two approaches:
- Create a task canceling the previous one if the system is active, rescheduling the new task.
- Create a periodic task and regularly check the last active time.
Which one is better?
Recommendations on how to improve my usage of synchronized, volatile, AtomicBoolean keywords in both approaches above are welcome.
public interface IHealthCheck {
void start();
void stop();
void onActive();
void onExpire();
}
public class Main {
public static void main(String[] args) {
IHealthCheck check = new HealthCheck2();
new Thread(() -> checkTask(check, 5)).start();
new Thread(() -> checkTask(check, 6)).start();
new Thread(() -> checkTask(check, 7)).start();
sleep(100);
check.start();
}
private static void checkTask(IHealthCheck check, long sleep) {
for (int i = 0; i < 1000; i++) {
sleep(sleep);
check.onActive();
}
}
private static void sleep(long n) {
try {
Thread.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.deneme;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class HealthCheck implements IHealthCheck {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> future;
@Override
public void start() {
onActive();
}
@Override
public synchronized void stop() {
if (future != null) {
future.cancel(true);
}
}
@Override
public synchronized void onActive() {
if (future != null) {
future.cancel(true);
}
System.out.println("Active: " + System.currentTimeMillis());
future = scheduler.schedule(this::onExpire, 200, TimeUnit.MILLISECONDS);
}
@Override
public void onExpire() {
System.out.println("Expired: " + System.currentTimeMillis());
}
}
package com.deneme;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class HealthCheck2 implements IHealthCheck {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> future;
private Instant lastActiveTime;
private boolean expired;
@Override
public void start() {
onActive();
future = scheduler.scheduleWithFixedDelay(this::check, 50, 50, TimeUnit.MILLISECONDS);
}
private synchronized void check() {
long diff = ChronoUnit.MILLIS.between(lastActiveTime, Instant.now());
if (diff > 100) {
onExpire();
}
}
@Override
public synchronized void stop() {
if (future != null) {
future.cancel(true);
}
}
@Override
public synchronized void onActive() {
expired = false;
lastActiveTime = Instant.now();
System.out.println("Active: " + lastActiveTime.toEpochMilli());
}
@Override
public void onExpire() {
if (!expired) {
expired = true;
System.out.println("Expired: " + System.currentTimeMillis());
}
}
}