3 Stimmen

Timeout auf einem Java-Cache

Wir haben einen einfachen, aber sehr häufig genutzten Cache, implementiert durch eine ConcurrentHashMap. Jetzt wollen wir alle Werte regelmäßig aktualisieren (zum Beispiel alle 15 Minuten).

Ich hätte gerne Code wie diesen:

 private void regularCacheCleanup() {
        final long now = System.currentTimeMillis();
        final long delta = now - cacheCleanupLastTime;
        if (delta < 0 || delta > 15 * 60 * 1000) {
            cacheCleanupLastTime = now;
            clearCache();
        }
  }

Außer dass es sein sollte:

  • Thread sicher
  • Nicht blockierend und extrem leistungsfähig, wenn der Cache nicht geleert werden muss
  • Keine Abhängigkeiten außer auf Java-Klassen (also kein Google CacheBuilder)
  • Absolut zuverlässig ;-)
  • Kann keine neuen Threads starten

Im Moment denke ich daran, einen kurzen Timer in einem ThreadLocal zu implementieren. Wenn dieser abläuft, wird der eigentliche Timer auf synchronisierte Weise überprüft. Das ist jedoch enorm viel Code, also wäre eine einfachere Idee schön.

5voto

Lefteris Laskaridis Punkte 2270

Der Mainstream-Weg, dieses Problem anzugehen, wäre die Verwendung eines Timer-Threads, um Ihren Cache in festgelegten Intervallen zu aktualisieren. Da Sie jedoch keine neuen Threads erstellen müssen, ist eine mögliche Implementierung, an die ich denke, die eines pseudo-zeitgesteuerten Cache-Refreshs. Grundsätzlich würde ich Überprüfungen in den Cache-Zugriffsmethoden (set und get Methoden) einfügen und jedes Mal, wenn Clients diese Methoden verwenden, überprüfen, ob der Cache aktualisiert werden muss, bevor die Set- oder Get-Aktion durchgeführt wird. Das ist die grobe Idee:

class YourCache {

  // hält den Zeitpunkt des letzten Cache-Refreshs in Millisekunden
  private volatile long lastRefreshDate;

  // gibt an, dass der Cache im Moment Einträge aktualisiert
  private volatile boolean cacheCurrentlyRefreshing;

  private Karte Cache = // Ihre gleichzeitige Karte Cache...

  public void put(Object key, Object element) {
    if (cacheNeedsRefresh()) {
      refresh();
    }
    Map.put(key, Element);
  }

  public Object get(Object key) {
    if (cacheNeedsRefresh()) {
      refresh();
    }
    return Map.get(key);
  }

  private boolean cacheNeedsRefresh() {
    // Stellen Sie sicher, dass der Cache nicht gerade von einem anderen Thread aktualisiert wird.
    if (cacheCurrentlyRefreshing) {
      return false;
    }
    return (now - lastRefreshDate) >= REFRESH_INTERVAL;
  }

  private void refresh() {
    // Stellen Sie sicher, dass der Cache zwischen CacheNeedsRefresh()
    // und Refresh() nicht von einem anderen Thread zu aktualisieren beginnt.
    if (cacheCurrentlyRefreshing) {
      return;
    }

    // Signal an andere Threads, dass der Cache im Moment aktualisiert wird.
    cacheCurrentlyRefreshing = true;

    try {
      // aktualisieren Sie hier den Cache-Inhalt
    } finally {
       // setzen Sie den lastRefreshDate und signalisieren Sie, dass der Cache fertig ist
       // aktualisieren Sie andere Threads.
       lastRefreshDate = System.currentTimeMillis();
       cahceCurrentlyRefreshing = false;
    }
  }
}

Persönlich würde ich es nicht so machen, aber wenn Sie keine Timer-Threads erstellen möchten oder können, könnte dies eine Option für Sie sein.

Beachten Sie, dass obwohl diese Implementierung das Sperren vermeidet, sie immer noch anfällig für doppelte Aktualisierungen aufgrund von Rennereignissen ist. Wenn dies für Ihre Anforderungen in Ordnung ist, sollte es kein Problem sein. Wenn Sie jedoch strengere Anforderungen haben, müssen Sie Sperren setzen, um die Threads ordnungsgemäß zu synchronisieren und Rennereignisse zu vermeiden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X