Java 1.4 Synchronisation: nur eine Instanz des Verfahrens ermöglichen (nicht blockierend) laufen zu lassen?

StackOverflow https://stackoverflow.com/questions/152138

Frage

Ich habe eine Klasse Übersetzungen Dienstprogramme vorschlagen. Die Übersetzungen selbst sollten alle 30 Minuten neu geladen werden. Ich benutze Frühling Timer Unterstützung dafür. Grundsätzlich meine Klasse wie folgt aussieht:

public interface Translator {
    public void loadTranslations();
    public String getTranslation(String key);
}

loadTranslations () kann ziemlich lang sein, so zu laufen, während es die alten Übersetzungen sind noch verfügbar läuft. Dies geschieht, indem die Übersetzungen in einer lokalen Karte laden und nur die Referenz zu ändern, wenn alle Übersetzungen geladen werden.

Mein Problem ist: Wie kann ich sicher sein, dass, wenn ein Thread bereits Übersetzungen Laden, ein zweites ist auch zu laufen versucht, es erkennt und kehrt sofort zurück, ohne ein zweites Update starten

.

Eine synchronisierte Methode wird nur die Lasten Warteschlange ... Ich bin immer noch auf Java 1.4, so dass kein java.util.concurrent.

Vielen Dank für Ihre Hilfe!

War es hilfreich?

Lösung

Verwenden Sie irgendeine Form Mechanismus nur von Sperren, die Aufgabe zu erfüllen, wenn es nicht bereits im Gang ist. das Verriegelungs Token erwerben muss ein einstufiger Prozeß sein. Siehe auch:

/**
 * @author McDowell
 */
public abstract class NonconcurrentTask implements Runnable {

    private boolean token = true;

    private synchronized boolean acquire() {
        boolean ret = token;
        token = false;
        return ret;
    }

    private synchronized void release() {
        token = true;
    }

    public final void run() {
        if (acquire()) {
            try {
                doTask();
            } finally {
                release();
            }
        }
    }

    protected abstract void doTask();

}

Test-Code, der eine Ausnahme ausgelöst wird, wenn die Aufgabe gleichzeitig ausgeführt wird:

public class Test {

    public static void main(String[] args) {
        final NonconcurrentTask shared = new NonconcurrentTask() {
            private boolean working = false;

            protected void doTask() {
                System.out.println("Working: "
                        + Thread.currentThread().getName());
                if (working) {
                    throw new IllegalStateException();
                }
                working = true;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (!working) {
                    throw new IllegalStateException();
                }
                working = false;
            }
        };

        Runnable taskWrapper = new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    shared.run();
                }
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(taskWrapper).start();
        }
    }

}

Andere Tipps

Ich bin von einem .net Hintergrund (keine Java-Erfahrung überhaupt), aber man konnte eine einfache statische Flagge irgendeine Art versuchen, die zu Beginn des Verfahrens, wenn seine alrady Laufprüft. Dann alles, was Sie tun müssen, um sicherzustellen, dass jeder Lese- / Schreib dieses Flag synchronisiert ist. Also am Anfang überprüfen Sie die Flagge, wenn es nicht gesetzt ist, setzen Sie ihn, wenn es gesetzt ist, Rückkehr. Wenn es nicht gesetzt ist, führen Sie den Rest des Verfahrens und nach dessen Abschluss ungesetzt es. So stellen Sie sicher, dass Sie den Code in einem Versuch setzen / finally und die Flagge iunsetting in der schließlich so ist es immer im Fehlerfall nicht gesetzt wird. Sehr vereinfacht kann aber alles, was Sie brauchen werden.

Edit: Dies ist eigentlich wahrscheinlich besser funktioniert als die Methode zu synchronisieren. Da brauchen Sie wirklich eine neue Übersetzung sofort nach dem einen, bevor es fertig ist? Und Sie dürfen nicht zu lange einen Thread sperren wollen, wenn es eine Weile warten muss.

einen Griff auf dem Last Faden halten, um zu sehen, ob es läuft?

Oder können Sie nicht nur eine synchronisierte Flag verwenden, um anzuzeigen, wenn eine Last im Gang ist?

Dies ist eigentlich identisch mit dem Code, der die Konstruktion eines Singleton zu verwalten erforderlich ist (Keuchen!), Wenn die klassische Art und Weise geschehen:

if (instance == null) {
  synchronized {
    if (instance == null) {
       instance = new SomeClass();
    }
  }
}

Der innere Test ist mit dem äußeren Test identisch. Der äußere Test ist so, dass wir routinemäßig einen synchronisierten Block, der innere Test zu bestätigen, einzugeben nicht, dass sich die Situation nicht geändert hat, seit wir zuletzt den Test gemacht (der Faden könnte vor dem Eintritt in Synchronisierte preempted wurden).

In Ihrem Fall:

if (translationsNeedLoading()) {
  synchronized {
    if (translationsNeedLoading()) {
       loadTranslations();
    }
  }
}

UPDATE: Auf diese Weise eine Singleton der Konstruktion nicht zuverlässig unter Ihrem JDK1.4 arbeiten. Zur Erläuterung hier . Aber ich denke, Sie sind Sie in diesem Szenario in Ordnung sein wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top