Java TimerTask运行时检查实现咨询及代码问题排查
Hey there! Let's work through your code step by step—you've got a solid goal in mind, but there are a few critical missteps keeping it from working right, plus some habits we can clean up to make your code more reliable.
The most critical issue is that all your scheduled check logic lives in the RepeatCheckerTask constructor. Remember: Timer.scheduleAtFixedRate is designed to run the run() method of your task on a fixed interval. The constructor only executes once, when you create the task instance. Right now, your code isn't running checks every 10 seconds—it's spawning hundreds of task instances (thanks to that loop in main) and running the constructor logic once per instance, which is a huge waste of resources and not what you want.
Let's break down the other issues:
- Overusing static variables:
counter,twoMinuteCounter, andnoDuplicatesare all static, meaning every task instance shares the same values. This leads to messy, unpredictable logic, and they're also thread-unsafe (no synchronization between your main thread and the timer's background thread). - Inefficient duplicate checking: Your nested loops have an O(n²) time complexity—slow, especially as your input list grows. A
Mapcan count duplicates in O(n) time instead. - Infinite task spawning: The
while(RepeatCheckerTask.twoMinuteCounter < 1)loop inmainkeeps callingscheduleAtFixedRate, creating a flood of task instances that will tank your CPU usage. - Unsafe state sharing: The static
noDuplicatesvariable isn't synchronized, so your main thread might not see changes made by the timer thread right away (or at all, in some cases).
Let's rewrite this to meet your goal: checking if any number is entered 5 times within a 2-minute sliding window, and stopping input if that happens. We'll fix the timer logic, add proper time tracking, and clean up the code.
import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; class RepeatCheckerTask extends TimerTask { // Helper class to track each number and when it was entered public static class NumberWithTimestamp { int value; long timestamp; public NumberWithTimestamp(int value, long timestamp) { this.value = value; this.timestamp = timestamp; } } private final List<NumberWithTimestamp> numberList; private final AtomicBoolean noDuplicates; private static final int WINDOW_SECONDS = 120; // 2 minutes private static final int DUPLICATE_LIMIT = 5; public RepeatCheckerTask(List<NumberWithTimestamp> numberList, AtomicBoolean noDuplicates) { this.numberList = numberList; this.noDuplicates = noDuplicates; } @Override public void run() { // Only run checks if we haven't already found a violation if (!noDuplicates.get()) return; long currentTime = System.currentTimeMillis(); Map<Integer, Integer> recentCount = new HashMap<>(); // Count numbers entered in the last 2 minutes for (NumberWithTimestamp entry : numberList) { if (currentTime - entry.timestamp <= WINDOW_SECONDS * 1000) { recentCount.put(entry.value, recentCount.getOrDefault(entry.value, 0) + 1); // Trigger violation if we hit the duplicate limit if (recentCount.get(entry.value) == DUPLICATE_LIMIT) { noDuplicates.set(false); System.out.println("Banned! You entered the same number 5 times within 2 minutes."); return; } } } } } public class Testing { public static void main(String[] args) { List<RepeatCheckerTask.NumberWithTimestamp> numberList = new ArrayList<>(); Scanner input = new Scanner(System.in); Timer timer = new Timer(); AtomicBoolean noDuplicates = new AtomicBoolean(true); final int MAX_INPUTS = 10; // Schedule the task ONCE to run every 10 seconds timer.scheduleAtFixedRate(new RepeatCheckerTask(numberList, noDuplicates), 0, 10000); System.out.println("Enter whole numbers (max 10, or you'll be banned for 5 duplicates in 2 minutes):"); while (input.hasNextInt() && noDuplicates.get() && numberList.size() < MAX_INPUTS) { int num = input.nextInt(); // Record the number and its entry time numberList.add(new RepeatCheckerTask.NumberWithTimestamp(num, System.currentTimeMillis())); if (numberList.size() == MAX_INPUTS) { System.out.println("Maximum number of inputs reached!"); System.out.println("Your inputs: " + numberList.stream() .map(n -> String.valueOf(n.value)) .collect(Collectors.joining(", "))); break; } } // Clean up resources timer.cancel(); input.close(); if (!noDuplicates.get()) { System.out.println("Stop! You've violated the duplicate rule."); } } }
- Proper TimerTask usage: All check logic is in the
run()method, which executes every 10 seconds as intended. - Sliding window tracking: We record each number's timestamp, so we only count entries from the last 2 minutes (exact, not just fixed 2-minute blocks).
- Thread-safe state:
AtomicBooleanensures the main thread and timer thread see the latest value ofnoDuplicateswithout synchronization issues. - Efficient counting: A
HashMapcounts duplicates in linear time, making the check fast even with larger lists. - Clean resource management: We cancel the timer and close the scanner when done, so no background threads linger.
- No static variables: All state is instance-specific or passed explicitly, avoiding shared state bugs.
For more flexible scheduling (and better reliability than Timer), consider using ScheduledExecutorService instead. It uses a thread pool and handles edge cases like long-running tasks better:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; // Replace the Timer code in main with this: ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(new RepeatCheckerTask(numberList, noDuplicates), 0, 10, TimeUnit.SECONDS); // Then clean up with: scheduler.shutdown();
内容的提问来源于stack exchange,提问作者Ulrich Batanado

