Heim  >  Artikel  >  Java  >  Lösung des Widerspruchs zwischen Singleton-Modus und Thread-Sicherheit unter Spring

Lösung des Widerspruchs zwischen Singleton-Modus und Thread-Sicherheit unter Spring

不言
不言nach vorne
2018-10-22 17:30:084426Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Lösung des Widerspruchs zwischen Singleton-Modus und Thread-Sicherheit. Er hat einen gewissen Referenzwert. Ich hoffe, er wird für Sie hilfreich sein.

Wie viele Menschen kennen oder ignorieren Multithreading-Probleme bei der Verwendung des Spring-Frameworks oft nicht?

Denn beim Schreiben eines Programms oder beim Durchführen von Unit-Tests ist es schwierig, auf Multithreading-Probleme zu stoßen, da es keine Umgebung gibt, die Multithreading-Tests einfach simulieren kann. Wenn dann mehrere Threads dieselbe Bean aufrufen, treten Thread-Sicherheitsprobleme auf. Wenn der Bean-Erstellungsmodus in Spring kein Singleton ist, tritt dieses Problem nicht auf.

Aber wenn Sie potenzielle Schwachstellen nicht berücksichtigen, wird es zu einem versteckten Killer des Programms, der ausbricht, wenn Sie es nicht wissen. Darüber hinaus ist es in der Regel sehr mühsam, ihn in der Produktionsumgebung auszulösen, wenn das Programm zur Verwendung bereitgestellt wird.

Spring verwendet ThreadLocal, um Thread-Sicherheitsprobleme zu lösen

Wir wissen, dass in einer Multithread-Umgebung im Allgemeinen nur zustandslose Beans gemeinsam genutzt werden können. In Spring können die meisten Beans als Singleton-Bereich deklariert werden. Dies liegt daran, dass Spring ThreadLocal verwendet, um nicht threadsichere Zustände in einigen Beans (z. B. RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder usw.) zu verarbeiten, wodurch sie threadsicher werden, da zustandsbehaftete Beans von mehreren Threads gemeinsam genutzt werden können.

Allgemeine Webanwendungen sind in drei Ebenen unterteilt: Präsentationsschicht, Serviceschicht und Persistenzschicht. Die entsprechende Logik wird in verschiedenen Schichten geschrieben, und die untere Schicht öffnet Funktionsaufrufe für die obere Schicht über Schnittstellen. Unter normalen Umständen gehören alle Programmaufrufe vom Empfang einer Anfrage bis zur Rückgabe einer Antwort zum selben Thread.

ThreadLocal ist eine gute Idee, um Thread-Sicherheitsprobleme zu lösen. Es löst den Konflikt des gleichzeitigen Zugriffs auf Variablen, indem es für jeden Thread eine unabhängige Kopie der Variablen bereitstellt. In vielen Fällen ist ThreadLocal einfacher und bequemer als die direkte Verwendung des synchronisierten Synchronisationsmechanismus zur Lösung von Thread-Sicherheitsproblemen, und das resultierende Programm weist eine höhere Parallelität auf.

Wenn in dem Prozess, in dem sich Ihr Code befindet, mehrere Threads gleichzeitig ausgeführt werden, führen diese Threads diesen Code möglicherweise gleichzeitig aus. Wenn die Ergebnisse jedes Laufs mit denen von Single-Thread-Läufen übereinstimmen und die Werte anderer Variablen mit den Erwartungen übereinstimmen, ist dies Thread-sicher. Mit anderen Worten: Die von einer Klasse oder einem Programm bereitgestellte Schnittstelle ist eine atomare Operation für Threads oder das Umschalten zwischen mehreren Threads führt nicht zu Mehrdeutigkeiten in den Ausführungsergebnissen der Schnittstelle, was bedeutet, dass wir keine Synchronisationsprobleme berücksichtigen müssen. Thread-Sicherheitsprobleme werden durch globale Variablen und statische Variablen verursacht.

Wenn in jedem Thread nur Lesevorgänge und keine Schreibvorgänge für globale Variablen und statische Variablen vorhanden sind, ist diese globale Variable im Allgemeinen threadsicher, wenn mehrere Threads gleichzeitig Schreibvorgänge ausführen Im Allgemeinen muss die Thread-Synchronisation berücksichtigt werden, da sonst die Thread-Sicherheit beeinträchtigt werden kann.
1) Konstanten sind immer Thread-sicher, da es nur Leseoperationen gibt.
2) Das Erstellen einer neuen Instanz vor jedem Methodenaufruf ist threadsicher, da nicht auf freigegebene Ressourcen zugegriffen wird.
3) Lokale Variablen sind threadsicher. Denn jedes Mal, wenn eine Methode ausgeführt wird, wird eine lokale Variable in einem separaten Bereich erstellt, der keine gemeinsam genutzte Ressource ist. Zu den lokalen Variablen gehören Methodenparametervariablen und methodeninterne Variablen.

Stateful zu sein bedeutet, über eine Datenspeicherfunktion zu verfügen. Stateful-Objekte (Stateful Beans) sind Objekte mit Instanzvariablen, die Daten speichern können und nicht threadsicher sind. Zwischen Methodenaufrufen wird kein Status beibehalten.

Stateless ist ein einmaliger Vorgang und kann keine Daten speichern. Ein zustandsloses Objekt (Stateless Bean) ist ein Objekt ohne Instanzvariablen. Es kann keine Daten speichern, ist eine unveränderliche Klasse und threadsicher.

Zustandsbehaftete Objekte:

Zustandslose Beans eignen sich für den unveränderlichen Modus. Die Technologie ist der Singleton-Modus, sodass Instanzen gemeinsam genutzt und die Leistung verbessert werden können. Stateful Beans sind in einer Multithread-Umgebung nicht sicher, daher ist der Prototype-Prototypmodus geeignet. Prototyp: Jede Anfrage für eine Bean erstellt eine neue Bean-Instanz.

Die Standardimplementierung von Struts2 ist der Prototype-Modus. Das heißt, für jede Anfrage wird eine neue Aktionsinstanz generiert, sodass kein Problem mit der Thread-Sicherheit besteht. Es ist zu beachten, dass, wenn Spring den Lebenszyklus der Aktion verwaltet, der Bereich als Prototyp-Bereich

Thread-Sicherheitsfall

SimpleDateFormat (im Folgenden bezeichnet) konfiguriert werden muss sdf)-Klassenintern Es gibt eine Kalenderobjektreferenz, die zum Speichern von Datumsinformationen im Zusammenhang mit diesem SDF verwendet wird, z. B. sdf.parse(dateStr), sdf.format(date) usw. Die datumsbezogene Zeichenfolge, das Datum, usw., die von den Methodenparametern übergeben werden, sind alle zu speichernden Kalenderreferenzen. Wenn Ihr SDF statisch ist, teilen sich mehrere Threads diese SDF und beobachten auch die sdf.parse ()-Methode finden Sie die folgenden Aufrufe:

 Date parse() {
   calendar.clear(); // 清理calendar
   ... // 执行一些操作, 设置 calendar 的日期什么的
   calendar.getTime(); // 获取calendar的时间
 }

Das Problem, das hier auftreten wird, ist, dass, wenn Thread A sdf.parse() aufruft und bevor Calendar.getTime() nach Calendar.clear() ausgeführt wird, Thread B sdf.parse() erneut aufruft Diesmal führte Thread B auch die Methode sdf.clear() aus, was dazu führte, dass die Kalenderdaten von Thread A gelöscht wurden (tatsächlich wurden A und B gleichzeitig gelöscht), oder wenn A den Kalender ausführte clear(). Zu diesem Zeitpunkt beginnt B mit dem Aufruf von sdf.parse() und endet reibungslos. Auf diese Weise wird das im Kalender von A gespeicherte Datum

des von B festgelegten Datums Dahinter verbirgt sich ein wichtigeres Problem – Staatenlosigkeit: Einer der Vorteile der zustandslosen Methode besteht darin, dass sie in verschiedenen Umgebungen sicher aufgerufen werden kann. Um zu messen, ob eine Methode zustandsbehaftet ist, hängt sie davon ab, ob sie andere Dinge ändert, beispielsweise globale Variablen und Instanzfelder. Die Formatmethode ändert das Kalenderfeld von SimpleDateFormat während des laufenden Prozesses, sodass es zustandsbehaftet ist.

Dies erinnert uns auch daran, bei der Entwicklung und Gestaltung des Systems auf die folgenden drei Punkte zu achten:

Wenn Sie selbst öffentliche Klassen schreiben, sollten Sie die Konsequenzen von Multithread-Aufrufen in Kommentaren deutlich beachten erklären

In einer Thread-Umgebung müssen wir auf die Thread-Sicherheit jeder gemeinsam genutzten Variablenvariablen achten

Beim Entwerfen unserer Klassen und Methoden sollten wir unser Bestes geben, um sie als nahtlosen Status

Lösung

1. Erstellen Sie bei Bedarf eine neue Instanz:

Hinweis: Verwenden Sie bei Bedarf SimpleDateFormat, wann immer Sie eine neue erstellen Wenn Sie beispielsweise an derselben Stelle ein Objekt mit Thread-Sicherheitsproblemen von „gemeinsam“ in „lokal privat“ ändern, können Multithreading-Probleme vermieden werden, es erhöht jedoch auch die Belastung beim Erstellen von Objekten. Unter normalen Umständen sind die Auswirkungen auf die Leistung nicht sehr offensichtlich.

2. Synchronisierung verwenden: SimpleDateFormat-Objekt synchronisieren

public class DateSyncUtil {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      
    public static String formatDate(Date date)throws ParseException{
        synchronized(sdf){
            return sdf.format(date);
        }  
    }
    
    public static Date parse(String strDate) throws ParseException{
        synchronized(sdf){
            return sdf.parse(strDate);
        }
    } 
}
Beschreibung: Wenn es viele Threads gibt und ein Thread diese Methode aufruft, möchten andere Threads diese Methode aufrufen Es ist notwendig, zu blockieren, wenn die Menge an Multithread-Parallelität groß ist, was einen gewissen Einfluss auf die Leistung hat.

3. Verwenden Sie ThreadLocal:

public class ConcurrentDateUtil {
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    public static Date parse(String dateStr) throws ParseException {
        return threadLocal.get().parse(dateStr);
    }
    public static String format(Date date) {
        return threadLocal.get().format(date);
    }
}
oder

ThreadLocal<DateFormat>(); 
 
    public static DateFormat getDateFormat()   
    {  
        DateFormat df = threadLocal.get();  
        if(df==null){  
            df = new SimpleDateFormat(date_format);  
            threadLocal.set(df);  
        }  
        return df;  
    }  
    public static String formatDate(Date date) throws ParseException {
        return getDateFormat().format(date);
    }
    public static Date parse(String strDate) throws ParseException {
        return getDateFormat().parse(strDate);
    }   
}
Hinweis: Die Verwendung von ThreadLocal macht auch gemeinsam genutzte Variablen exklusiv, und die Thread-Exklusivität wird definitiv mit der Methode verglichen Exklusivität kann den Aufwand für die Erstellung von Objekten in einer gleichzeitigen Umgebung erheblich reduzieren. Bei relativ hohen Leistungsanforderungen empfiehlt sich grundsätzlich diese Methode.

4. Verzichten Sie auf JDK und verwenden Sie Zeitformatierungsklassen in anderen Bibliotheken:

Verwenden Sie FastDateFormat in Apache Commons und behaupten, es sei schnell und threadsicher. SimpleDateFormat ist leider möglich Formatiert nur Datumsangaben und kann keine Datumszeichenfolgen analysieren.

Verwenden Sie die Joda-Time-Klassenbibliothek, um zeitbezogene Probleme zu lösen

Methode eins ist die langsamste und Methode drei ist die schnellste. Aber selbst die langsamste Methode ist schlecht Die Leistung ist nicht schlecht, die allgemeinen Systemmethoden 1 und 2 können erfüllt werden, sodass es an dieser Stelle schwierig ist, zum Engpass Ihres Systems zu werden. Aus einfacher Sicht wird empfohlen, Methode eins oder Methode zwei zu verwenden. Wenn Sie bei Bedarf eine kleine Leistungsverbesserung anstreben, können Sie Methode drei in Betracht ziehen und ThreadLocal zum Caching verwenden.

Die Joda-Time-Klassenbibliothek eignet sich perfekt für die Zeitverarbeitung und wird zur Verwendung empfohlen.

Zusammenfassung

Zurück zur Frage am Anfang des Artikels: „Wie viele Leute kennen oder ignorieren Multithreading-Probleme oft nicht, wenn sie das Spring-Framework verwenden?“ ?" 》

Tatsächlich kann jeder Code schreiben. Warum unterscheidet sich der vom Architekten geschriebene Code so sehr von Ihrem? Es sollte ein so kleines Problem sein, an das Sie nicht gedacht haben, aber der Architekt hat es berücksichtigt.

Architekten verfügen über ein breiteres Wissen, haben spezifischere Situationen gesehen und verfügen über umfangreichere Erfahrungen bei der Lösung verschiedener Probleme. Werden Sie, solange Sie das Denken und die Gewohnheiten eines Architekten entwickeln, noch weit davon entfernt sein, ein Architekt zu sein?


Das obige ist der detaillierte Inhalt vonLösung des Widerspruchs zwischen Singleton-Modus und Thread-Sicherheit unter Spring. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen