package com.zy.core.thread.support; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongSupplier; public class RecentStationArrivalTracker { static final long DEFAULT_RETAIN_MS = 10_000L; private final long retainMs; private final LongSupplier nowSupplier; private final Map arrivalTimeMap = new ConcurrentHashMap<>(); private final AtomicLong lastCleanupAt = new AtomicLong(0L); public RecentStationArrivalTracker() { this(DEFAULT_RETAIN_MS, System::currentTimeMillis); } public RecentStationArrivalTracker(long retainMs, LongSupplier nowSupplier) { this.retainMs = retainMs <= 0L ? DEFAULT_RETAIN_MS : retainMs; this.nowSupplier = nowSupplier == null ? System::currentTimeMillis : nowSupplier; } public void observe(Integer stationId, Integer taskNo, boolean loading) { if (!loading) { return; } record(stationId, taskNo); } public void record(Integer stationId, Integer taskNo) { Long key = buildKey(stationId, taskNo); if (key == null) { return; } long now = nowSupplier.getAsLong(); arrivalTimeMap.put(key, now); cleanupIfNeeded(now); } public boolean hasRecentArrival(Integer stationId, Integer taskNo) { Long key = buildKey(stationId, taskNo); if (key == null) { return false; } long now = nowSupplier.getAsLong(); Long arrivalAt = arrivalTimeMap.get(key); if (arrivalAt == null) { cleanupIfNeeded(now); return false; } if (now - arrivalAt > retainMs) { arrivalTimeMap.remove(key, arrivalAt); cleanupIfNeeded(now); return false; } cleanupIfNeeded(now); return true; } private void cleanupIfNeeded(long now) { long lastCleanup = lastCleanupAt.get(); if (now - lastCleanup < retainMs || !lastCleanupAt.compareAndSet(lastCleanup, now)) { return; } for (Map.Entry entry : arrivalTimeMap.entrySet()) { if (entry == null || now - entry.getValue() <= retainMs) { continue; } arrivalTimeMap.remove(entry.getKey(), entry.getValue()); } } private Long buildKey(Integer stationId, Integer taskNo) { if (stationId == null || stationId <= 0 || taskNo == null || taskNo <= 0) { return null; } return (((long) stationId) << 32) | (taskNo.longValue() & 0xffffffffL); } }