


Programmation simultanée Java : principe de mise en œuvre du conteneur simultané CopyOnWriteArrayList
Copy-On-Write, appelé COW, est une stratégie d'optimisation utilisée en programmation. L'idée de base est que tout le monde partage le même contenu depuis le début. Lorsque quelqu'un souhaite modifier le contenu, il le copie pour former un nouveau contenu, puis le modifie. C'est une sorte de stratégie de paresse différée. À partir du JDK 1.5, le package de concurrence Java fournit deux conteneurs simultanés implémentés à l'aide du mécanisme CopyOnWrite, à savoir CopyOnWriteArrayList et CopyOnWriteArraySet. Le conteneur CopyOnWrite est très utile et peut être utilisé dans de nombreux scénarios simultanés.
Qu'est-ce qu'un conteneur CopyOnWrite ?
Un conteneur CopyOnWrite est un conteneur qui est copié lors de l'écriture. La compréhension populaire est que lorsque nous ajoutons des éléments à un conteneur, nous ne les ajoutons pas directement au conteneur actuel, mais copions d'abord le conteneur actuel pour créer un nouveau conteneur, puis ajoutons des éléments au nouveau conteneur. Pointez ensuite la référence du conteneur d'origine vers le nouveau conteneur. L'avantage de ceci est que nous pouvons effectuer des lectures simultanées sur le conteneur CopyOnWrite sans verrouillage, car le conteneur actuel n'ajoutera aucun élément. Par conséquent, le conteneur CopyOnWrite est également une idée de séparation de la lecture et de l'écriture, et la lecture et l'écriture sont des conteneurs différents.
Le principe d'implémentation de CopyOnWriteArrayList
Avant d'utiliser CopyOnWriteArrayList, lisons son code source pour comprendre comment il est implémenté. Le code suivant est l'implémentation de la méthode add dans CopyOnWriteArrayList (ajout d'éléments à CopyOnWriteArrayList). Vous pouvez constater que vous devez verrouiller lors de l'ajout, sinon N copies seront copiées lors de l'écriture avec plusieurs threads.
2
|
/**
* Ajoute l'élément spécifié à la fin de cette liste.
*
* @param e élément à ajouter à cette liste
* @return <tt>true</tt> (comme spécifié par {@link Collection#add})
*/
public booléen add(E e) {
final Verrou ReentrantLock = this .lock;
lock.lock();
essayez {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1 );
newElements[len] = e;
setArray(newElements);
retour vrai ;
} enfin {
lock.unlock();
}
}
|
1 2 3 |
public E get( int index) {
return get(getArray(), index);
}
|
Il n'y a pas de CopyOnWriteMap fourni dans le JDK. On peut se référer à CopyOnWriteArrayList pour en implémenter un. Le code de base est le suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
importer
java.util.Collection;
importer
java.util.Map;
importer
java.util.Set;
public
class
CopyOnWriteMap<K, V>
impléments
Map<K, V>, Clonable {
privé
volatile
Carte<K, V> internalMap;
public
CopyOnWriteMap() {
internalMap =
nouveau
HashMap<K, V>();
}
public
V put (clé K, valeur V) {
synchronisé
(
ce
) {
Carte<K, V> newMap =
new
HashMap<K, V>(internalMap);
V val = newMap.put(clé, valeur);
internalMap = newMap;
retour
val;
}
}
public
V get(Clé d'objet) {
retour
internalMap.get(key);
}
public
void
putAll(Map<?
étend
K , ?
étend
V> newData) {
synchronisé
(
ce
) {
Carte<K, V> newMap =
new
HashMap<K, V>(internalMap);
newMap.putAll(newData);
internalMap = newMap;
}
}
}
L'implémentation est très simple. Tant que nous comprenons le mécanisme CopyOnWrite, nous pouvons implémenter divers conteneurs CopyOnWrite et les utiliser dans différents scénarios d'application.
Scénarios d'application de CopyOnWrite
Le conteneur simultané CopyOnWrite est utilisé dans des scénarios simultanés avec plus de lecture et moins d'écriture. Par exemple, des scénarios d'accès et de mise à jour de liste blanche, de liste noire et de catégorie de produits. Si nous avons un site Web de recherche, l'utilisateur saisit des mots-clés pour rechercher du contenu dans la zone de recherche de ce site Web, mais la recherche de certains mots-clés n'est pas autorisée. Ces mots-clés non recherchables seront placés dans une liste noire, mise à jour chaque nuit. Lorsque l'utilisateur effectue une recherche, il vérifiera si le mot-clé actuel est dans la liste noire. Si tel est le cas, il indiquera que la recherche ne peut pas être effectuée. Le code d'implémentation est le suivant :
|
package com.ifeve.book;
importer java.util.Map;
importer com.ifeve.book.forkjoin.CopyOnWriteMap;
/**
* 黑名单服务
*
* @auteur fangtengfei
*
*/
public class BlackListServiceImpl {
privé statique CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(
1000 );
public static boolean isBlackList(String id) {
retour blackListMap.get(id) == null faux : vrai ;
}
public static void addBlackList(String id) {
blackListMap.put(id, Boolean.TRUE);
}
/**
* Ajouter une liste noire par lots
*
* @identifiants param
*/
public static void addBlackList(Map<String,Boolean> ids) { code>
|
Le code est très simple, mais il y a deux choses auxquelles vous devez faire attention lorsque vous utilisez CopyOnWriteMap :
1. Réduisez la surcharge d'expansion. Initialisez la taille de CopyOnWriteMap en fonction des besoins réels pour éviter la surcharge de l'expansion de CopyOnWriteMap lors de l'écriture.
2. Utilisez l'ajout par lots. Parce que chaque fois que vous ajoutez, le conteneur sera copié à chaque fois, donc réduire le nombre d'ajouts peut réduire le nombre de fois que le conteneur est copié. Par exemple, utilisez la méthode addBlackList dans le code ci-dessus.
Inconvénients de CopyOnWrite
Le conteneur CopyOnWrite présente de nombreux avantages, mais il existe également deux problèmes, à savoir l'utilisation de la mémoire et la cohérence des données. Vous devez donc y prêter attention lors du développement.
Problème d'utilisation de la mémoire. En raison du mécanisme de copie sur écriture de CopyOnWrite, lorsqu'une opération d'écriture est effectuée, deux objets résideront simultanément dans la mémoire, l'ancien objet et le nouvel objet écrit (remarque : lors de la copie, seules les références dans le conteneur sont copiés. Uniquement lors de l'écriture, de nouveaux objets seront créés et ajoutés au nouveau conteneur, tandis que les objets de l'ancien conteneur sont toujours utilisés, il y a donc deux copies de la mémoire des objets). Si ces objets occupent une quantité de mémoire relativement importante, par exemple environ 200 Mo, alors l'écriture de 100 Mo supplémentaires de données occupera 300 Mo de mémoire, ce qui peut entraîner des Yong GC et des Full GC fréquents. Auparavant, nous utilisions un service dans notre système qui utilisait le mécanisme CopyOnWrite pour mettre à jour les objets volumineux chaque nuit, ce qui entraînait un GC complet de 15 secondes chaque nuit, et le temps de réponse de l'application devenait également plus long.
Compte tenu du problème d'utilisation de la mémoire, vous pouvez réduire la consommation de mémoire des objets volumineux en compressant les éléments dans le conteneur. Par exemple, si les éléments sont tous des nombres décimaux, vous pouvez envisager de les compresser en hexadécimal ou en nombres décimaux. Nombres hexadécimaux Base 64. Ou n'utilisez pas le conteneur CopyOnWrite, mais utilisez d'autres conteneurs simultanés, tels que ConcurrentHashMap.
Problème de cohérence des données. Le conteneur CopyOnWrite ne peut garantir que la cohérence finale des données, mais ne peut garantir la cohérence en temps réel des données. Donc, si vous souhaitez que les données écrites soient lues immédiatement, veuillez ne pas utiliser le conteneur CopyOnWrite.
Articles connexes :
Programmation simultanée Java : CountDownLatch, CyclicBarrier et Semaphore
[Pratique de programmation simultanée JAVA] Séquence de verrouillage Deadlock
Vidéos associées :
Tutoriel vidéo sur les applications avancées de la bibliothèque multithread et simultanée Java
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)

Sujets chauds

La gestion des exceptions de fonction en C++ est particulièrement importante pour les environnements multithread afin de garantir la sécurité des threads et l’intégrité des données. L'instruction try-catch vous permet d'intercepter et de gérer des types spécifiques d'exceptions lorsqu'elles se produisent afin d'éviter les plantages du programme ou la corruption des données.

Les techniques de concurrence et de multithreading utilisant les fonctions Java peuvent améliorer les performances des applications, notamment en suivant les étapes suivantes : Comprendre les concepts de concurrence et de multithreading. Tirez parti des bibliothèques de concurrence et multithread de Java telles que ExecutorService et Callable. Pratiquez des cas tels que la multiplication matricielle multithread pour réduire considérablement le temps d'exécution. Profitez des avantages d’une vitesse de réponse accrue des applications et d’une efficacité de traitement optimisée grâce à la concurrence et au multithreading.

La concurrence et les coroutines sont utilisées dans la conception GoAPI pour : Traitement hautes performances : traiter plusieurs requêtes simultanément pour améliorer les performances. Traitement asynchrone : utilisez des coroutines pour traiter des tâches (telles que l'envoi d'e-mails) de manière asynchrone, libérant ainsi le thread principal. Traitement des flux : utilisez des coroutines pour traiter efficacement les flux de données (tels que les lectures de bases de données).

Le multithreading PHP fait référence à l'exécution simultanée de plusieurs tâches dans un seul processus, ce qui est réalisé en créant des threads exécutés indépendamment. Vous pouvez utiliser l'extension Pthreads en PHP pour simuler le comportement multi-threading. Après l'installation, vous pouvez utiliser la classe Thread pour créer et démarrer des threads. Par exemple, lors du traitement d'une grande quantité de données, les données peuvent être divisées en plusieurs blocs et un nombre correspondant de threads peut être créé pour un traitement simultané afin d'améliorer l'efficacité.

Les mutex sont utilisés en C++ pour gérer des ressources partagées multithread : créez des mutex via std::mutex. Utilisez mtx.lock() pour obtenir un mutex et fournir un accès exclusif aux ressources partagées. Utilisez mtx.unlock() pour libérer le mutex.

Dans un environnement multithread, la gestion de la mémoire C++ est confrontée aux défis suivants : courses de données, blocages et fuites de mémoire. Les contre-mesures incluent : 1. L'utilisation de mécanismes de synchronisation, tels que les mutex et les variables atomiques ; 2. L'utilisation de structures de données sans verrouillage ; 3. L'utilisation de pointeurs intelligents ; 4. (Facultatif) La mise en œuvre du garbage collection ;

Les tests unitaires des fonctions simultanées sont essentiels car cela permet de garantir leur comportement correct dans un environnement simultané. Des principes fondamentaux tels que l'exclusion mutuelle, la synchronisation et l'isolement doivent être pris en compte lors du test de fonctions concurrentes. Les fonctions simultanées peuvent être testées unitairement en simulant, en testant les conditions de concurrence et en vérifiant les résultats.

Les tests de programmes multithread sont confrontés à des défis tels que la non-répétabilité, les erreurs de concurrence, les blocages et le manque de visibilité. Les stratégies incluent : Tests unitaires : écrivez des tests unitaires pour chaque thread afin de vérifier le comportement du thread. Simulation multithread : utilisez un framework de simulation pour tester votre programme en contrôlant la planification des threads. Détection de courses aux données : utilisez des outils pour trouver des courses aux données potentielles, tels que valgrind. Débogage : utilisez un débogueur (tel que gdb) pour examiner l'état du programme d'exécution et trouver la source de la course aux données.
