Ich habe eine Spring-Bean deklariert, die meinen Email-Server alle paar Sekunden abfragt. Wenn eine Mail vorhanden ist, wird sie abgerufen und versucht, alle angehängten Dateien darin zu extrahieren. Diese Dateien werden dann an einen Uploader gesendet, der sie sicher speichert. Der Uploader wird auch als Spring Bean deklariert. Eine dritte Bean verknüpft den Absender der E-Mail mit dem Dateinamen der Datei und speichert diesen in einer Datenbank.
Es stellte sich heraus, dass, als ein paar Leute gleichzeitig versuchten, E-Mails zu verschicken, eine Menge unordentlicher Dinge passierten. Datensätze in der DB haben falsche Dateinamen. Einige haben überhaupt keine Dateinamen erhalten.
Ich habe das Problem der Tatsache zugeschrieben, dass Bohnen standardmäßig auf Singleton beschränkt sind. Dies bedeutet, dass eine Reihe von Threads wahrscheinlich mit ein und derselben Instanz zur gleichen Zeit versagt. Die Frage ist, wie man das löst.
Wenn ich alle sensitiven Methoden synchronisiere, dann stapeln sich alle Threads und warten auf einander, was gegen die Idee des Multithreading steht.
Auf der anderen Seite wird das Festlegen der Bean auf "request" neue Instanzen von jedem von ihnen erstellen, was auch nicht wirklich gut ist, wenn wir über Speicherverbrauch und Thread-Planung sprechen.
Ich bin verwirrt. Was soll ich tun?
Ich stimme den Antworten von @Bozho und @stivio zu.
Die bevorzugten Optionen bestehen darin, den Status "Store no" in einem Singleton-Bereich zu übergeben und ein Context-Objekt an die Methoden zu übergeben oder einen Prototyp / Request-Bereichs-Beans zu verwenden, die für jeden Verarbeitungszyklus erstellt werden. Die Synchronisierung kann normalerweise vermieden werden, indem Sie einen dieser Ansätze wählen, und Sie erhalten viel mehr Leistung, während Sie Deadlocks vermeiden. Stellen Sie nur sicher, dass Sie keinen gemeinsamen Status wie statische Member ändern.
Für jeden Ansatz gibt es Vor- und Nachteile:
Ich gehe in den meisten einfachen Fällen mit dem Service-Ansatz. Sie können diese Singleton-Beans auch ein Verarbeitungsobjekt erstellen lassen, das den Status für die Berechnung enthält. Dies ist eine Lösung, die Ihnen bei komplexeren Szenarien am besten dienen kann.
Bearbeiten: Es gibt Fälle, in denen Sie eine Singleton-Bean abhängig vom Prototyp-Bean im Bean haben und für jeden Methodenaufruf eine neue Instanz der Prototyp-Bean wünschen. Spring liefert dafür mehrere Lösungen:
Der erste verwendet Methodeninjektion , wie in der Spring-Referenzdokumentation beschrieben. Ich mag diesen Ansatz nicht wirklich, da er deine Klasse dazu zwingt, abstrakt zu sein.
Die zweite Möglichkeit besteht darin, ein ServiceLocatorFactoryBean oder Ihre eigene Factory-Klasse (die mit den Abhängigkeiten injiziert werden muss und einen Konstruktor aufrufen muss). Dieser Ansatz funktioniert in den meisten Fällen sehr gut und verbindet dich nicht mit Spring.
Es gibt Fälle, in denen Sie möchten, dass die Prototyp-Beans Laufzeitabhängigkeiten haben. Ein guter Freund von mir schrieb hier einen guten Beitrag: Ссылка .
Abstrakt gesprochen: Wenn Sie Spring Integration verwenden, sollten Sie Ihren Code in Bezug auf die Nachrichten selbst erstellen. ZB sollte jeder wichtige Status mit den Nachrichten propagiert werden. Dies macht es trivial, horizontal zu skalieren, indem mehr Spring Integration-Instanzen hinzugefügt werden, um die Last zu bewältigen. Der einzige Zustand (wirklich) in Spring Integration ist für Komponenten wie der Aggregator, der Nachrichten wartet und sammelt, die eine Korrelation haben. In diesem Fall können Sie an einen Sicherungsspeicher wie MongoDB delegieren, um den Speicher dieser Nachrichten zu verwalten, und das ist natürlich Thread-sicher.
Im Allgemeinen ist dies ein Beispiel für eine gestaffelte ereignisgesteuerte Architektur - Komponenten müssen statuslos (N (1), egal wie viele Nachrichten) mit Nachrichten umgehen und sie dann auf einem Kanal zum Verbrauch durch eine andere Komponente weiterleiten, von der nichts bekannt ist die vorherige Komponente, von der die Nachricht kam.
Wenn Sie mit der Spring-Integration Probleme mit der Thread-Sicherheit haben, könnten Sie etwas anderes tun als beabsichtigt und es könnte sich lohnen, Ihren Ansatz zu überdenken ...
Singletons sollten statusbehaftet und threadsicher sein.
Wenn ein Singleton statuslos ist, ist es ein degenerierter Fall, dass er statusbehaftet ist und threadsicher ist trivialerweise wahr. Aber was ist der Sinn Singleton? Erstellen Sie einfach jedes Mal, wenn jemand anfragt, ein neues.
Wenn eine Instanz statusbehaftet und nicht threadsicher ist, darf sie nicht Singleton sein; Jeder Thread sollte ausschließlich eine andere Instanz haben.
Tags und Links java multithreading spring