Table des matières
Qu'est-ce qu'un conteneur CopyOnWrite ?
Le principe d'implémentation de CopyOnWriteArrayList
Scénarios d'application de CopyOnWrite
Inconvénients de CopyOnWrite
Maison Java javaDidacticiel Programmation simultanée Java : principe de mise en œuvre du conteneur simultané CopyOnWriteArrayList

Programmation simultanée Java : principe de mise en œuvre du conteneur simultané CopyOnWriteArrayList

Jul 30, 2018 am 11:41 AM
多线程 并发

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.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

     * Appends the specified element to the end of this list.

     *

     * @param e element to be appended to this list

     * @return <tt>true</tt> (as specified by {@link Collection#add})

     */

    public boolean add(E e) {

    final ReentrantLock lock = this.lock;

    lock.lock();

    try {

        Object[] elements = getArray();

        int len = elements.length;

        Object[] newElements = Arrays.copyOf(elements, len + 1);

        newElements[len] = e;

        setArray(newElements);

        return true;

    finally {

        lock.unlock();

    }

    }

1

2

1

2

3

public E get(int index) {

    return get(getArray(), index);

}

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/**      * 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(); } }
Il n'est pas nécessaire de verrouiller lors de la lecture. Si plusieurs threads ajoutent des données à CopyOnWriteArrayList lors de la lecture, la lecture lira toujours les anciennes données, car lors de l'écriture de l'ancienne CopyOnWriteArrayList. ne sera pas verrouillé.
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 :

<. td>

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

import java.util.Collection;

import java.util.Map;

import java.util.Set;

 

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {

    private volatile Map<K, V> internalMap;

 

    public CopyOnWriteMap() {

        internalMap = new HashMap<K, V>();

    }

 

    public V put(K key, V value) {

 

        synchronized (this) {

            Map<K, V> newMap = new HashMap<K, V>(internalMap);

            V val = newMap.put(key, value);

            internalMap = newMap;

            return val;

        }

    }

 

    public V get(Object key) {

        return internalMap.get(key);

    }

 

    public void putAll(Map<? extends K, ? extends V> newData) {

        synchronized (this) {

            Map<K, V> newMap = new HashMap<K, V>(internalMap);

            newMap.putAll(newData);

            internalMap = newMap;

        }

    }

}

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 :

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

34

35

package com.ifeve.book;

 

import java.util.Map;

 

import com.ifeve.book.forkjoin.CopyOnWriteMap;

 

/**

 * 黑名单服务

 *

 * @author fangtengfei

 *

 */

public class BlackListServiceImpl {

 

    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(

            1000);

 

    public static boolean isBlackList(String id) {

        return blackListMap.get(id) == null false true;

    }

 

    public static void addBlackList(String id) {

        blackListMap.put(id, Boolean.TRUE);

    }

 

    /**

     * 批量添加黑名单

     *

     * @param ids

     */

    public static void addBlackList(Map<String,Boolean> ids) {

        blackListMap.putAll(ids);

    }

 

}

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 34 35
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> blackListMap.putAll(ids); } }

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!

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

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.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Repo: Comment relancer ses coéquipiers
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Combien de temps faut-il pour battre Split Fiction?
3 Il y a quelques semaines By DDD

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)

Exceptions de fonctions C++ et multithreading : gestion des erreurs dans les environnements concurrents Exceptions de fonctions C++ et multithreading : gestion des erreurs dans les environnements concurrents May 04, 2024 pm 04:42 PM

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.

Comment la concurrence et le multithreading des fonctions Java peuvent-ils améliorer les performances ? Comment la concurrence et le multithreading des fonctions Java peuvent-ils améliorer les performances ? Apr 26, 2024 pm 04:15 PM

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.

Application de la concurrence et des coroutines dans la conception de l'API Golang Application de la concurrence et des coroutines dans la conception de l'API Golang May 07, 2024 pm 06:51 PM

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).

Comment implémenter le multi-threading en PHP ? Comment implémenter le multi-threading en PHP ? May 06, 2024 pm 09:54 PM

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é.

Comment gérer les ressources partagées en multi-threading en C++ ? Comment gérer les ressources partagées en multi-threading en C++ ? Jun 03, 2024 am 10:28 AM

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.

Défis et contre-mesures de la gestion de la mémoire C++ dans un environnement multithread ? Défis et contre-mesures de la gestion de la mémoire C++ dans un environnement multithread ? Jun 05, 2024 pm 01:08 PM

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 ;

Un guide pour les tests unitaires des fonctions simultanées Go Un guide pour les tests unitaires des fonctions simultanées Go May 03, 2024 am 10:54 AM

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.

Défis et stratégies pour tester les programmes multithread en C++ Défis et stratégies pour tester les programmes multithread en C++ May 31, 2024 pm 06:34 PM

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.

See all articles