国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Maison Java JavaBase Introduction à l'analyse des principes Java CAS

Introduction à l'analyse des principes Java CAS

Dec 24, 2020 pm 05:37 PM
cas java concurrent

Tutoriel de base JavaIntroduction et analyse des colonnes Java CAS

Introduction à l'analyse des principes Java CAS

Recommandé (gratuit)?: Tutoriel de base Java

1 Introduction

CAS signifie comparer et échanger A. mécanisme pour implémenter la fonctionnalité de synchronisation dans un environnement multithread. Une opération CAS contient trois opérandes?: un emplacement mémoire, une valeur attendue et une nouvelle valeur. La logique d'implémentation de CAS consiste à comparer la valeur à l'emplacement mémoire avec la valeur attendue. Si elles sont égales, remplacez la valeur à l'emplacement mémoire par la nouvelle valeur. S'il n'est pas égal, aucune opération n'est effectuée.

En Java, Java n'implémente pas directement CAS. Les implémentations liées à CAS sont implémentées sous la forme d'un assemblage en ligne C++. Le code Java doit être appelé via JNI. J'analyserai les détails de mise en ?uvre au chapitre 3.

Comme mentionné précédemment, le processus de fonctionnement du CAS n'est pas difficile. Mais l’explication ci-dessus ne suffit pas. Ensuite, je présenterai d’autres connaissances de base. Ce n’est qu’avec ces connaissances de base que nous pourrons mieux comprendre le contenu ultérieur.

2. Introduction générale

Nous savons tous que le processeur transmet les données via le bus et la mémoire. à l'ère du multic?ur, plusieurs c?urs communiquent avec la mémoire et d'autres matériels via le même bus. Comme indiqué ci-dessous?:

Introduction à lanalyse des principes Java CAS

Source de l'image?: "Compréhension approfondie des systèmes informatiques"

L'image ci-dessus est un diagramme de structure informatique relativement simple, bien que simple. , il suffit d'expliquer la question. Dans le schéma ci-dessus, le CPU communique avec la mémoire via le bus marqué par les deux flèches bleues. Considérons une question?: si plusieurs c?urs du processeur fonctionnent sur la même mémoire en même temps, quels types d'erreurs se produiront si elle n'est pas contr?lée?? Voici une brève explication, en supposant que le noyau 1 écrit des données 64 bits dans la mémoire via un bus de bande passante de 32 bits, le noyau 1 doit écrire deux fois pour terminer l'ensemble de l'opération. Si, après que le noyau 1 ait écrit des données 32 bits pour la première fois, le noyau 2 lit les données 64 bits à partir de l'emplacement mémoire écrit par le noyau 1. étant donné que le noyau 1 n'a pas complètement écrit toutes les données 64 bits dans la mémoire, le noyau 2 commence à lire les données à partir de cet emplacement mémoire, les données lues doivent donc être chaotiques.

Mais il n’y a en réalité aucune raison de s’inquiéter de ce problème. Grace au manuel du développeur Intel, nous pouvons apprendre qu'à partir des processeurs Pentium, les processeurs Intel assureront la lecture et l'écriture atomiques de quadwords alignés sur des limites de 64 bits.

Sur la base de la description ci-dessus, nous pouvons conclure que les processeurs Intel peuvent garantir que les instructions alignées sur la mémoire à accès unique sont exécutées de manière atomique. Mais que se passe-t-il s’il s’agit d’une instruction pour accéder deux fois à la mémoire ? La réponse n’est pas garantie. Par exemple, l'instruction d'incrémentation inc dword ptr [...] est équivalente à DEST = DEST + 1. Cette instruction contient trois opérations 讀->改->寫, impliquant deux accès mémoire. Considérons une situation dans laquelle une valeur de 1 est stockée à un emplacement spécifié en mémoire. Désormais, les deux c?urs du processeur exécutent l’instruction en même temps. Le processus d'exécution alternée des deux c?urs est le suivant?:

  1. Le noyau 1 lit la valeur 1 à partir de l'emplacement spécifié en mémoire et la charge dans le registre.
  2. Le noyau 2 lit depuis l'emplacement spécifié dans la mémoire Valeur 1 et chargez-le dans le registre
  3. Core 1 Décrémentez la valeur dans le registre de 1
  4. Core 2 Décrémentez la valeur dans le registre de 1
  5. Core 1 écrit la valeur modifiée en mémoire
  6. Core 2 réécrit la valeur modifiée en mémoire

Après avoir exécuté le processus ci-dessus, la valeur finale dans la mémoire est 2, et ce à quoi nous nous attendions est 3, c'est ce qui se passe Problème. Pour résoudre ce problème, il est nécessaire d’empêcher deux ou plusieurs c?urs d’exploiter la même zone mémoire en même temps. Alors comment l’éviter ? Ceci présente le protagoniste de cet article – le préfixe de verrouillage. Pour une description détaillée de cette instruction, veuillez vous référer au Manuel du développeur Intel, volume 2, référence du jeu d'instructions, chapitre 3, référence du jeu d'instructions A-L. J'en cite ici une section, comme suit?:

LOCK—Assert LOCK# Signal Prefix
Provoque l'affirmation du signal LOCK# du processeur lors de l'exécution de l'instruction qui l'accompagne (transforme l'instruction en une instruction atomique). Dans un environnement multiprocesseur, le signal LOCK# garantit que le processeur a l'usage exclusif de toute mémoire partagée pendant que le signal est affirmé.

Les points clés décrits ci-dessus ont été utilisés. Il est souligné en gras que dans un environnement multiprocesseur, le signal LOCK# peut garantir que le processeur a l'usage exclusif d'une partie de la mémoire partagée. le verrou peut être ajouté avant les instructions suivantes?:

ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD , et XCHG.

En ajoutant le préfixe lock avant l'instruction inc, vous pouvez rendre l'instruction atomique. Lorsque plusieurs c?urs exécutent la même instruction inc en même temps, ils le feront de manière série, évitant ainsi la situation mentionnée ci-dessus. Il y a donc une autre question ici : comment le préfixe de verrouillage garantit-il que le noyau occupe exclusivement une certaine zone mémoire ? La réponse est la suivante?:

Dans les processeurs Intel, il existe deux manières de garantir qu'un certain c?ur du processeur occupe exclusivement une certaine zone mémoire. La première consiste à verrouiller le bus et à laisser un certain nombre de personnes utiliser le bus exclusivement, mais cela co?te trop cher. Une fois le bus verrouillé, les autres c?urs ne peuvent pas accéder à la mémoire, ce qui peut empêcher les autres c?urs de fonctionner pendant une courte période. La deuxième méthode consiste à verrouiller le cache si certaines données de la mémoire sont mises en cache dans le cache du processeur. Le signal LOCK# émis par le processeur ne verrouille pas le bus, mais verrouille la zone mémoire correspondant à la ligne de cache. Les autres processeurs ne peuvent pas effectuer d'opérations associées sur cette zone mémoire tant que cette zone mémoire est verrouillée. Par rapport au verrouillage du bus, le co?t du verrouillage du cache est évidemment inférieur. Concernant les verrous de bus et de cache, pour une description plus détaillée, veuillez vous référer au Manuel du développeur Intel Volume 3 Manuel du développeur de logiciels, Chapitre 8 Gestion de plusieurs processeurs.

3. Analyse du code source

Avec les connaissances de base ci-dessus, nous pouvons désormais lire tranquillement le code source de CAS. Le contenu de ce chapitre analysera la méthode compareAndSet dans la classe atomique AtomicInteger sous le package java.util.concurrent.atomic L'analyse pertinente est la suivante?:

public?class?AtomicInteger?extends?Number?implements?java.io.Serializable?{

????//?setup?to?use?Unsafe.compareAndSwapInt?for?updates
????private?static?final?Unsafe?unsafe?=?Unsafe.getUnsafe();
????private?static?final?long?valueOffset;

????static?{
????????try?{
????????????//?計算變量?value?在類對象中的偏移
????????????valueOffset?=?unsafe.objectFieldOffset
????????????????(AtomicInteger.class.getDeclaredField("value"));
????????}?catch?(Exception?ex)?{?throw?new?Error(ex);?}
????}

????private?volatile?int?value;
????
????public?final?boolean?compareAndSet(int?expect,?int?update)?{
????????/*
?????????*?compareAndSet?實際上只是一個殼子,主要的邏輯封裝在?Unsafe?的?
?????????*?compareAndSwapInt?方法中
?????????*/
????????return?unsafe.compareAndSwapInt(this,?valueOffset,?expect,?update);
????}
????
????//?......
}

public?final?class?Unsafe?{
????//?compareAndSwapInt?是?native?類型的方法,繼續(xù)往下看
????public?final?native?boolean?compareAndSwapInt(Object?o,?long?offset,
??????????????????????????????????????????????????int?expected,
??????????????????????????????????????????????????int?x);
????//?......
}
//?unsafe.cpp
/*
?*?這個看起來好像不像一個函數(shù),不過不用擔(dān)心,不是重點。UNSAFE_ENTRY?和?UNSAFE_END?都是宏,
?*?在預(yù)編譯期間會被替換成真正的代碼。下面的?jboolean、jlong?和?jint?等是一些類型定義(typedef):
?*?
?*?jni.h
?*?????typedef?unsigned?char???jboolean;
?*?????typedef?unsigned?short??jchar;
?*?????typedef?short???????????jshort;
?*?????typedef?float???????????jfloat;
?*?????typedef?double??????????jdouble;
?*?
?*?jni_md.h
?*?????typedef?int?jint;
?*?????#ifdef?_LP64?//?64-bit
?*?????typedef?long?jlong;
?*?????#else
?*?????typedef?long?long?jlong;
?*?????#endif
?*?????typedef?signed?char?jbyte;
?*/
UNSAFE_ENTRY(jboolean,?Unsafe_CompareAndSwapInt(JNIEnv?*env,?jobject?unsafe,?jobject?obj,?jlong?offset,?jint?e,?jint?x))
??UnsafeWrapper("Unsafe_CompareAndSwapInt");
??oop?p?=?JNIHandles::resolve(obj);
??//?根據(jù)偏移量,計算?value?的地址。這里的?offset?就是?AtomaicInteger?中的?valueOffset
??jint*?addr?=?(jint?*)?index_oop_from_field_offset_long(p,?offset);
??//?調(diào)用?Atomic?中的函數(shù)?cmpxchg,該函數(shù)聲明于?Atomic.hpp?中
??return?(jint)(Atomic::cmpxchg(x,?addr,?e))?==?e;
UNSAFE_END

//?atomic.cpp
unsigned?Atomic::cmpxchg(unsigned?int?exchange_value,
?????????????????????????volatile?unsigned?int*?dest,?unsigned?int?compare_value)?{
??assert(sizeof(unsigned?int)?==?sizeof(jint),?"more?work?to?do");
??/*
???*?根據(jù)操作系統(tǒng)類型調(diào)用不同平臺下的重載函數(shù),這個在預(yù)編譯期間編譯器會決定調(diào)用哪個平臺下的重載
???*?函數(shù)。相關(guān)的預(yù)編譯邏輯如下:
???*?
???*?atomic.inline.hpp:
???*????#include?"runtime/atomic.hpp"
???*????
???*????//?Linux
???*????#ifdef?TARGET_OS_ARCH_linux_x86
???*????#?include?"atomic_linux_x86.inline.hpp"
???*????#endif
???*???
???*????//?省略部分代碼
???*????
???*????//?Windows
???*????#ifdef?TARGET_OS_ARCH_windows_x86
???*????#?include?"atomic_windows_x86.inline.hpp"
???*????#endif
???*????
???*????//?BSD
???*????#ifdef?TARGET_OS_ARCH_bsd_x86
???*????#?include?"atomic_bsd_x86.inline.hpp"
???*????#endif
???*?
???*?接下來分析?atomic_windows_x86.inline.hpp?中的?cmpxchg?函數(shù)實現(xiàn)
???*/
??return?(unsigned?int)Atomic::cmpxchg((jint)exchange_value,?(volatile?jint*)dest,
???????????????????????????????????????(jint)compare_value);
}

L'analyse ci-dessus semble être longue, mais. le processus principal n’est pas compliqué. Si vous ne vous attardez pas sur les détails du code, il est relativement facile à comprendre. Ensuite, j'analyserai la fonction Atomic::cmpxchg sous la plateforme Windows. Continuez à lire.

//?atomic_windows_x86.inline.hpp
#define?LOCK_IF_MP(mp)?__asm?cmp?mp,?0??\
???????????????????????__asm?je?L0??????\
???????????????????????__asm?_emit?0xF0?\
???????????????????????__asm?L0:
??????????????
inline?jint?Atomic::cmpxchg?(jint?exchange_value,?volatile?jint*?dest,?jint?compare_value)?{
??//?alternative?for?InterlockedCompareExchange
??int?mp?=?os::is_MP();
??__asm?{
????mov?edx,?dest
????mov?ecx,?exchange_value
????mov?eax,?compare_value
????LOCK_IF_MP(mp)
????cmpxchg?dword?ptr?[edx],?ecx
??}
}

Le code ci-dessus est constitué de l'identifiant précompilé LOCK_IF_MP et de la fonction cmpxchg. Pour y voir un peu plus clair, rempla?ons LOCK_IF_MP dans la fonction cmpxchg par le contenu réel. Comme suit :

inline?jint?Atomic::cmpxchg?(jint?exchange_value,?volatile?jint*?dest,?jint?compare_value)?{
??//?判斷是否是多核?CPU
??int?mp?=?os::is_MP();
??__asm?{
????//?將參數(shù)值放入寄存器中
????mov?edx,?dest????//?注意:?dest?是指針類型,這里是把內(nèi)存地址存入?edx?寄存器中
????mov?ecx,?exchange_value
????mov?eax,?compare_value
????
????//?LOCK_IF_MP
????cmp?mp,?0
????/*
?????*?如果?mp?=?0,表明是線程運行在單核?CPU?環(huán)境下。此時?je?會跳轉(zhuǎn)到?L0?標(biāo)記處,
?????*?也就是越過?_emit?0xF0?指令,直接執(zhí)行?cmpxchg?指令。也就是不在下面的?cmpxchg?指令
?????*?前加?lock?前綴。
?????*/
????je?L0
????/*
?????*?0xF0?是?lock?前綴的機器碼,這里沒有使用?lock,而是直接使用了機器碼的形式。至于這樣做的
?????*?原因可以參考知乎的一個回答:
?????*?????https://www.zhihu.com/question/50878124/answer/123099923
?????*/?
????_emit?0xF0
L0:
????/*
?????*?比較并交換。簡單解釋一下下面這條指令,熟悉匯編的朋友可以略過下面的解釋:
?????*???cmpxchg:?即“比較并交換”指令
?????*???dword:?全稱是?double?word,在?x86/x64?體系中,一個?
?????*??????????word?=?2?byte,dword?=?4?byte?=?32?bit
?????*???ptr:?全稱是?pointer,與前面的?dword?連起來使用,表明訪問的內(nèi)存單元是一個雙字單元
?????*???[edx]:?[...]?表示一個內(nèi)存單元,edx?是寄存器,dest?指針值存放在?edx?中。
?????*??????????那么?[edx]?表示內(nèi)存地址為?dest?的內(nèi)存單元
?????*??????????
?????*?這一條指令的意思就是,將?eax?寄存器中的值(compare_value)與?[edx]?雙字內(nèi)存單元中的值
?????*?進行對比,如果相同,則將?ecx?寄存器中的值(exchange_value)存入?[edx]?內(nèi)存單元中。
?????*/
????cmpxchg?dword?ptr?[edx],?ecx
??}
}

Ceci conclut le processus de mise en ?uvre de CAS La mise en ?uvre de CAS est indissociable du support du processeur. Il y a tellement de codes ci-dessus, mais le code principal est en fait une instruction cmpxchg avec un préfixe de verrouillage, c'est-à-dire lock cmpxchg dword ptr [edx], ecx.

4. Problèmes ABA

Quand on parle de CAS, nous devons essentiellement parler des problèmes ABA du CAS. CAS se compose de trois étapes, à savoir "lecture->comparer->écriture". Considérons une situation où le thread 1 et le thread 2 exécutent la logique CAS en même temps. La séquence d'exécution des deux threads est la suivante?:

  1. Temps 1?: le thread 1 effectue une opération de lecture et obtient l'original. valeur A, puis le thread Switched away
  2. Time 2?: Thread 2 termine l'opération CAS et modifie la valeur d'origine de A à B
  3. Time 3?: Thread 2 effectue à nouveau l'opération CAS et change la valeur d'origine de B en A
  4. Moment 4?: le fil 1 reprend son exécution, compare la valeur de comparaison (compareValue) avec la valeur d'origine (oldValue) et constate que les deux valeurs sont égales. Ensuite, écrivez la nouvelle valeur (newValue) dans la mémoire pour terminer l'opération CAS

comme dans le processus ci-dessus. Le fil de discussion 1 ne sait pas que la valeur d'origine a été modifiée. à son avis, il y en a. aucun changement, donc le processus continuera à s'exécuter. Pour les problèmes ABA, la solution habituelle consiste à définir un numéro de version pour chaque opération CAS. Le package java.util.concurrent.atomic fournit une classe atomique AtomicStampedReference qui peut gérer les problèmes ABA. L'implémentation spécifique ne sera pas analysée ici. Les amis intéressés peuvent la vérifier par eux-mêmes.

5. Résumé

En écrivant ceci, cet article touche enfin à sa fin. Bien que le principe du CAS lui-même, y compris sa mise en ?uvre, ne soit pas difficile, il n’est vraiment pas facile à écrire. Cela implique des connaissances de bas niveau. Même si je peux le comprendre, c'est quand même un peu difficile à comprendre. En raison de mon manque de connaissances sous-jacentes, certaines des analyses ci-dessus seront inévitablement fausses. Donc, s'il y a une erreur, n'hésitez pas à commenter. Bien s?r, il est préférable d'expliquer pourquoi c'est faux. Merci.

D’accord, c’est tout pour cet article. Merci d'avoir lu et au revoir.

Annexe

Les chemins de plusieurs fichiers utilisés dans la section précédente d'analyse du code source sont publiés ici. Cela aidera tout le monde à indexer, comme suit?:

文件名 路徑
Unsafe.java openjdk/jdk/src/share/classes/sun/misc/Unsafe.java
unsafe.cpp openjdk/hotspot/src/share/vm/prims/unsafe.cpp
atomic.cpp openjdk/hotspot/src/share/vm/runtime/atomic.cpp
atomic_windows_x86.inline.hpp openjdk/hotspot/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp

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!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefa?on, veuillez contacter admin@php.cn

Outils d'IA chauds

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

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

Sujets chauds

Tutoriel PHP
1502
276
Comment gérer les transactions en Java avec JDBC? Comment gérer les transactions en Java avec JDBC? Aug 02, 2025 pm 12:29 PM

Pour gérer correctement les transactions JDBC, vous devez d'abord désactiver le mode de validation automatique, puis effectuer plusieurs opérations, et enfin vous engager ou randonner en fonction des résultats; 1. Appelez Conn.SetAutoCommit (false) pour démarrer la transaction; 2. Exécuter plusieurs opérations SQL, telles que l'insertion et la mise à jour; 3. Appelez Conn.Commit () Si toutes les opérations sont réussies, et appelez Conn.Rollback () Si une exception se produit pour garantir la cohérence des données; Dans le même temps, les ressources TRY-With doivent être utilisées pour gérer les ressources, gérer correctement les exceptions et cl?turer les connexions pour éviter la fuite de connexion; De plus, il est recommandé d'utiliser des pools de connexion et de définir des points de sauvegarde pour réaliser un retour en arrière partiel, et de maintenir les transactions aussi courtes que possible pour améliorer les performances.

Comment travailler avec le calendrier à Java? Comment travailler avec le calendrier à Java? Aug 02, 2025 am 02:38 AM

Utilisez des classes dans le package Java.Time pour remplacer les anciennes classes de date et de calendrier; 2. Obtenez la date et l'heure actuelles via LocalDate, LocalDateTime et Localtime; 3. Créez une date et une heure spécifiques en utilisant la méthode OF (); 4. Utilisez la méthode plus / moins pour augmenter et diminuer le temps; 5. Utilisez ZonedDateTime et ZoneID pour traiter le fuseau horaire; 6. Format et cha?nes de date d'analyse via DateTimeFormatter; 7. Utilisez instantanément pour être compatible avec les anciens types de dates si nécessaire; Le traitement des dattes dans le Java moderne devrait donner la priorité à l'utilisation de Java.timeapi, qui fournit clairement, immuable et linéaire

Comparaison des frameworks Java: Spring Boot vs Quarkus vs MicronAut Comparaison des frameworks Java: Spring Boot vs Quarkus vs MicronAut Aug 04, 2025 pm 12:48 PM

Pré-formancetartuptimemoryusage, quarkusandmicronautleadduetocompile-timeprocessingandgraalvsupport, withquarkusofperforming lightbetterine scénarios.

Comment fonctionne la collection Garbage en Java? Comment fonctionne la collection Garbage en Java? Aug 02, 2025 pm 01:55 PM

La collecte des ordures de Java (GC) est un mécanisme qui gère automatiquement la mémoire, ce qui réduit le risque de fuite de mémoire en récupérant des objets inaccessibles. 1. GC juge l'accessibilité de l'objet de l'objet racine (tel que les variables de pile, les threads actifs, les champs statiques, etc.), et les objets inaccessibles sont marqués comme des ordures. 2. Sur la base de l'algorithme de compensation de marque, marquez tous les objets accessibles et effacez des objets non marqués. 3. Adopter une stratégie de collecte générationnelle: la nouvelle génération (Eden, S0, S1) exécute fréquemment MinorGC; Les personnes agées fonctionnent moins, mais prend plus de temps pour effectuer MajorGC; Metaspace Stores Metadata de classe. 4. JVM fournit une variété de périphériques GC: SerialGC convient aux petites applications; Le parallelGC améliore le débit; CMS réduit

Passez l'exemple de l'exemple de journalisation du middleware http Passez l'exemple de l'exemple de journalisation du middleware http Aug 03, 2025 am 11:35 AM

HTTP Log Middleware dans GO peut enregistrer les méthodes de demande, les chemins de requête, la propriété intellectuelle du client et le temps qui prend du temps. 1. Utilisez http.handlerfunc pour envelopper le processeur, 2. Enregistrez l'heure de début et l'heure de fin avant et après l'appel Suivant.Servehttp, 3. Obtenez le vrai client IP via R.RemoteAddr et X-Forwared-For Headers, 4. Utilisez le log.printf aux journaux de demande de sortie, 5. L'exemple de code complet a été vérifié pour s'exécuter et convient au démarrage d'un projet petit et moyen. Les suggestions d'extension incluent la capture des codes d'état, la prise en charge des journaux JSON et le suivi des ID de demande.

Utilisation de types HTML ?Entrée? pour les données utilisateur Utilisation de types HTML ?Entrée? pour les données utilisateur Aug 03, 2025 am 11:07 AM

Le choix du bon type HTMLinput peut améliorer la précision des données, améliorer l'expérience utilisateur et améliorer la convivialité. 1. Sélectionnez les types d'entrée correspondants en fonction du type de données, tels que le texte, le courrier électronique, le tel, le numéro et la date, qui peuvent vérifier automatiquement la somme de la somme et l'adaptation au clavier; 2. Utilisez HTML5 pour ajouter de nouveaux types tels que l'URL, la couleur, la plage et la recherche, qui peuvent fournir une méthode d'interaction plus intuitive; 3. Utilisez l'espace réservé et les attributs requis pour améliorer l'efficacité et la précision du remplissage des formulaires, mais il convient de noter que l'espace réservé ne peut pas remplacer l'étiquette.

Comparaison des outils de construction Java: Maven vs Gradle Comparaison des outils de construction Java: Maven vs Gradle Aug 03, 2025 pm 01:36 PM

GradleisthebetterChoiceFormostNewProjectsDuetOtsSuperiorflexibility, Performance et ModerNtoolingSupport.1.gradle’sgroovy / kotlindslismoreConcis

passer par l'exemple de déclaration de différence expliquée passer par l'exemple de déclaration de différence expliquée Aug 02, 2025 am 06:26 AM

Le report est utilisé pour effectuer des opérations spécifiées avant le retour de la fonction, telles que les ressources de nettoyage; Les paramètres sont évalués immédiatement lorsqu'ils sont reportés et les fonctions sont exécutées dans l'ordre de la dernière entrée (LIFO); 1. Plusieurs éleveurs sont exécutés dans l'ordre inverse des déclarations; 2. Communément utilisé pour le nettoyage sécurisé tel que la fermeture des fichiers; 3. La valeur de retour nommée peut être modifiée; 4. Il sera exécuté même si la panique se produit, adaptée à la récupération; 5. éviter l'abus de report dans les boucles pour éviter la fuite des ressources; Une utilisation correcte peut améliorer la sécurité et la lisibilité du code.

See all articles