


Résoudre la contradiction entre le mode singleton et la sécurité des threads sous Spring
Le contenu de cet article vise à résoudre la contradiction entre le mode singleton et la sécurité des threads sous Spring. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Combien de personnes ne connaissent pas ou ignorent les problèmes de multi-threading lorsqu'elles utilisent le framework Spring ?
Parce que lors de l'écriture d'un programme ou de la réalisation de tests unitaires, il est difficile de rencontrer des problèmes multi-thread car il n'existe pas d'environnement capable de simuler facilement des tests multi-thread. Ensuite, lorsque plusieurs threads appellent le même bean, il y aura des problèmes de sécurité des threads. Si le mode de création du bean au Spring n'est pas singleton, il n'y aura pas de problème de ce type.
Mais si vous ne tenez pas compte des vulnérabilités potentielles, il deviendra un tueur caché du programme, éclatant lorsque vous ne le savez pas. De plus, il est généralement très difficile de le déclencher dans l'environnement de production lorsque le programme est livré pour utilisation.
Spring utilise ThreadLocal pour résoudre les problèmes de sécurité des threads
Nous savons qu'en général, seuls les beans sans état peuvent être partagés dans un environnement multithread. Au Spring, la plupart des beans peuvent être déclarés comme portée singleton. En effet, Spring utilise ThreadLocal pour traiter les états non thread-safe dans certains beans (tels que RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder, etc.), ce qui les rend thread-safe, car les beans avec état peuvent être partagés entre plusieurs threads.
Les applications Web générales sont divisées en trois niveaux : la couche de présentation, la couche de service et la couche de persistance. La logique correspondante est écrite dans différentes couches, et la couche inférieure ouvre les appels de fonction vers la couche supérieure via des interfaces. Dans des circonstances normales, tous les appels de programme, depuis la réception d'une requête jusqu'au retour d'une réponse, appartiennent au même thread.
ThreadLocal est une bonne idée pour résoudre les problèmes de sécurité des threads. Il résout le conflit d'accès simultané aux variables en fournissant une copie indépendante de la variable pour chaque thread. Dans de nombreux cas, ThreadLocal est plus simple et plus pratique que l'utilisation directe du mécanisme de synchronisation synchronisée pour résoudre les problèmes de sécurité des threads, et le programme résultant a une concurrence plus élevée.
S'il y a plusieurs threads exécutés en même temps dans le processus où se trouve votre code, ces threads peuvent exécuter ce code en même temps. Si les résultats de chaque exécution sont les mêmes que ceux des exécutions monothread et que les valeurs des autres variables sont les mêmes que celles attendues, cela est thread-safe. En d'autres termes : l'interface fournie par une classe ou un programme est une opération atomique pour les threads ou le basculement entre plusieurs threads ne provoquera pas d'ambiguïté dans les résultats d'exécution de l'interface, ce qui signifie que nous n'avons pas besoin de prendre en compte les problèmes de synchronisation. Les problèmes de sécurité des threads sont causés par des variables globales et des variables statiques.
S'il n'y a que des opérations de lecture et aucune opération d'écriture sur les variables globales et les variables statiques dans chaque thread, d'une manière générale, cette variable globale est thread-safe si plusieurs threads effectuent des opérations d'écriture en même temps ; , la synchronisation des threads doit généralement être prise en compte, sinon la sécurité des threads peut être affectée.
1) Les constantes sont toujours thread-safe car il n'y a que des opérations de lecture.
2) La création d'une nouvelle instance avant chaque appel de méthode est thread-safe car les ressources partagées ne seront pas accessibles.
3) Les variables locales sont thread-safe. Car à chaque fois qu'une méthode est exécutée, une variable locale sera créée dans un espace séparé, qui n'est pas une ressource partagée. Les variables locales incluent les variables de paramètres de méthode et les variables internes à la méthode.
Être avec état signifie avoir une fonction de stockage de données. Les objets avec état (Stateful Beans) sont des objets avec des variables d'instance qui peuvent enregistrer des données et ne sont pas thread-safe. Aucun état n'est conservé entre les appels de méthode.
Apatride est une opération unique et ne peut pas enregistrer de données. Un objet sans état (Stateless Bean) est un objet sans variables d'instance. Il ne peut pas enregistrer de données, c'est une classe immuable et il est thread-safe.
Objets avec état :
Les beans sans état sont adaptés au mode immuable. La technologie est en mode singleton, afin que les instances puissent être partagées et les performances améliorées. Les beans avec état ne sont pas sûrs dans un environnement multithread, le mode prototype Prototype est donc adapté. Prototype : Chaque demande de bean crée une nouvelle instance de bean.
L'implémentation par défaut de Struts2 est le mode Prototype. Autrement dit, une nouvelle instance d'action est générée pour chaque requête, il n'y a donc aucun problème de sécurité des threads. A noter que si Spring gère le cycle de vie de l'action, le scope doit être configuré en scope prototype
Thread safety case
SimpleDateFormat (ci-après dénommé sdf) classe interne Il existe une référence d'objet Calendar, qui est utilisée pour stocker les informations de date liées à ce sdf, telles que sdf.parse(dateStr), sdf.format(date) et ainsi de suite. etc. transmis par les paramètres de méthode sont tous des amis. Référence de calendrier à stocker. Cela posera un problème si votre sdf est statique, alors plusieurs threads partageront ce sdf, et en même temps partageront cette référence de calendrier, et observeront le. Méthode sdf.parse(), Vous retrouverez les appels suivants :
Date parse() { calendar.clear(); // 清理calendar ... // 执行一些操作, 设置 calendar 的日期什么的 calendar.getTime(); // 获取calendar的时间 }
Le problème qui se posera ici est que si le thread A appelle sdf.parse(), et avant que Calendar.getTime() ne soit exécuté après Calendar.clear(), le thread B appelle à nouveau sdf .parse(), à ce moment-là, le thread B a également exécuté la méthode sdf.clear(), ce qui a entraîné l'effacement des données de calendrier du thread A (en fait, A et B ont été effacés en même temps ou lorsque A exécuté, il est suspendu après Calendar.clear). (). À ce moment-là, B commence à appeler sdf.parse() et se termine en douceur, de cette façon, la date stockée dans le calendrier de A devient la date du calendrier défini par B plus tard
Derrière ce problème se cache. un problème plus important est celui de l'apatridie : l'un des avantages de la méthode apatride est qu'elle peut être appelée en toute sécurité dans divers environnements. Pour mesurer si une méthode est avec état, cela dépend si elle modifie d'autres éléments, tels que les variables globales et les champs d'instance. La méthode format modifie le champ de calendrier de SimpleDateFormat pendant le processus en cours, il est donc avec état.
Cela nous rappelle également de prêter attention aux trois points suivants lors du développement et de la conception du système :
Lorsque vous écrivez vous-même des cours publics, vous devez clairement noter les conséquences des appels multithread dans les commentaires expliquer
Dans un environnement de thread, nous devons faire attention à la sécurité des threads de chaque variable partagée
Lors de la conception de nos classes et méthodes, nous devons faire de notre mieux pour les concevoir de manière transparente. Statut
Solution
1. Créez une nouvelle instance si nécessaire :
Remarque : utilisez SimpleDateFormat si nécessaire chaque fois que vous créez une nouvelle instance. Par exemple au même endroit, changer un objet présentant des problèmes de sécurité des threads de partagé à localement privé peut éviter les problèmes de multithread, mais cela augmente également la charge de création d'objets. Dans des circonstances normales, l’impact sur les performances n’est pas très évident.
2. Utiliser la synchronisation : synchroniser l'objet SimpleDateFormat
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); } } }
Description : Lorsqu'il y a plusieurs threads, lorsqu'un thread appelle cette méthode, d'autres threads veulent l'appeler. méthode Les threads doivent être bloqués. Lorsque la quantité de concurrence multithread est importante, cela aura un certain impact sur les performances.
3. Utilisez 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); } }
ou
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); } }
Remarque : L'utilisation de ThreadLocal rend également les variables partagées exclusives. réduire le coût de création d'objets dans un environnement concurrent par rapport à l'exclusivité de la méthode. Si les exigences de performances sont relativement élevées, cette méthode est généralement recommandée.
4. Abandonnez le JDK et utilisez des classes de formatage de l'heure dans d'autres bibliothèques :
Utilisez FastDateFormat dans Apache commons, prétendant être SimpleDateFormat rapide et sécurisé pour les threads, malheureusement c'est possible. formate uniquement les dates et ne peut pas analyser les chaînes de date.
Utilisez la bibliothèque de classes Joda-Time pour gérer les problèmes liés au temps
Faites un test de stress simple. La première méthode est la plus lente et la troisième est la plus rapide. Mais même la méthode la plus lente est médiocre. performances. Pas mal, les méthodes générales 1 et 2 du système peuvent être satisfaites, il est donc difficile de devenir le goulot d'étranglement de votre système à ce stade. D'un point de vue simple, il est recommandé d'utiliser la première ou la deuxième méthode. Si vous souhaitez améliorer légèrement les performances lorsque cela est nécessaire, vous pouvez envisager d'utiliser la troisième méthode, en utilisant ThreadLocal pour la mise en cache.
La bibliothèque de classes Joda-Time est parfaite pour le traitement du temps et son utilisation est recommandée.
Résumé
Retour à la question du début de l'article : "Combien de personnes ne connaissent pas ou ignorent souvent les problèmes de multi-threading lors de l'utilisation du framework Spring ?" 》
En fait, n'importe qui peut écrire du code. Pourquoi l'effet du code écrit par l'architecte est-il si différent du vôtre ? Cela devrait être ce genre de petit problème auquel vous n'avez pas pensé mais que l'architecte a pris en compte.
Les architectes ont des connaissances plus larges, ont vu des situations plus spécifiques et ont une expérience plus riche dans la résolution de divers problèmes. Tant que vous développerez la pensée et les habitudes d’un architecte, serez-vous encore loin d’être un architecte ?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Guide du générateur de nombres aléatoires en Java. Nous discutons ici des fonctions en Java avec des exemples et de deux générateurs différents avec d'autres exemples.

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

Guide de TimeStamp to Date en Java. Ici, nous discutons également de l'introduction et de la façon de convertir l'horodatage en date en Java avec des exemples.

Java est un langage de programmation populaire qui peut être appris aussi bien par les développeurs débutants que par les développeurs expérimentés. Ce didacticiel commence par les concepts de base et progresse vers des sujets avancés. Après avoir installé le kit de développement Java, vous pouvez vous entraîner à la programmation en créant un simple programme « Hello, World ! ». Une fois que vous avez compris le code, utilisez l'invite de commande pour compiler et exécuter le programme, et « Hello, World ! » s'affichera sur la console. L'apprentissage de Java commence votre parcours de programmation et, à mesure que votre maîtrise s'approfondit, vous pouvez créer des applications plus complexes.
