Programmation concurrente en Java Brian Goetz

Résumé

La programmation concurrente permet l'exécution de programmes en parallèle. A l'heure où les processeurs multicoeurs sont devenus un standard, elle est désormais incontournable, et concerne tous les développeurs Java. Mais l'écriture d'un code qui exploite efficacement la puissance des nouveaux processeurs et supporte les environnements concurrents représente un défi à la fois en termes d'architecture, de programmation et de tests. Le développement, le test et le débogage d'applications multithreads s'avèrent en effet très ardus car, évidemment, les problèmes de concurrence se manifestent de façon imprévisible. Ils apparaissent généralement au pire moment - en production, sous une lourde charge de travail. Le but de ce livre est de répondre à ces défis en offrant des techniques, des patrons et des outils pour analyser les programmes et pour encapsuler la complexité des interactions concurrentes. Il fournit à la fois les bases théoriques et les techniques concrètes pour construire des applications concurrentes fiables et adaptées aux systèmes actuels - et futurs.

Éditeur :
Pearson,
Genre :
Manuel
Langue :
français.
Description du livre original :
1 vol (369 p.)
ISBN :
9782744025082.
Domaine public :
Non
Téléchargement du livre au format PDF pour « Programmation concurrente en Java »

Table des matières

  • Préface
  • Préface à l'édition française
  • Structure de l'ouvrage
  • Exemples de code
  • Remerciements
  • Introduction
    • 1.1 Bref historique de la programmation concurrente
    • 1.2 Avantages des threads
      • 1.2.1 Exploitation de plusieurs processeurs
      • 1.2.2 Simplicité de la modélisation
      • 1.2.3 Gestion simplifiée des événements asynchrones
      • 1.2.4 Interfaces utilisateur plus réactives
    • 1.3 Risques des threads
      • 1.3.1 Risques concernant la "thread safety"
      • 1.3.2 Risques sur la vivacité
      • 1.3.3 Risques sur les performances
    • 1.4 Les threads sont partout
      • 2.1 Qu'est-ce que la thread safety ?
        • 2.1.1 Exemple : une servlet sans état
      • 2.2 Atomicité
        • 2.2.1 Situations de compétition
        • 2.2.2 Exemple : situations de compétition dans une initialisation paresseuse
        • 2.2.3 Actions composées
      • 2.3 Verrous
        • 2.3.1 Verrous internes
        • 2.3.2 Réentrance
      • 2.4 Protection de l'état avec les verrous
      • 2.5 Vivacité et performances
    • 3.1 Visibilité
      • 3.1.1 Données obsolètes
        • 3.1.2 Opérations 64 bits non atomiques
        • 3.1.3 Verrous et visibilité
        • 3.1.4 Variables volatiles
      • 3.2 Publication et fuite
        • 3.2.1 Pratiques de construction sûres
      • 3.3 Confinement des objets
        • 3.3.1 Confinement ad hoc
        • 3.3.2 Confinement dans la pile
        • 3.3.3 ThreadLocal
      • 3.4 Objets non modifiables
        • 3.4.1 Champs final
        • 3.4.2 Exemple : utilisation de volatile pour publier des objets non modifiables
      • 3.5 Publication sûre
        • 3.5.1 Publication incorrecte : quand les bons objets deviennent mauvais
        • 3.5.2 Objets non modifiables et initialisation sûre
        • 3.5.3 Idiomes de publication correcte
        • 3.5.4 Objets non modifiables dans les faits
        • 3.5.5 Objets modifiables
        • 3.5.6 Partage d'objets de façon sûre
      • 4.1 Conception d'une classe thread-safe
        • 4.1.1 Exigences de synchronisation
        • 4.1.2 Opérations dépendantes de l'état
        • 4.1.3 Appartenance de l'état
      • 4.2 Confinement des instances
        • 4.2.1 Le patron moniteur de Java
        • 4.2.2 Exemple : gestion d'une flotte de véhicules
      • 4.3 Délégation de la thread safety
        • 4.3.1 Exemple : gestionnaire de véhicules utilisant la délégation
        • 4.3.2 Variables d'état indépendantes
        • 4.3.3 Échecs de la délégation
        • 4.3.4 Publication des variables d'état sous-jacentes
        • 4.3.5 Exemple : gestionnaire de véhicules publiant son état
      • 4.4 Ajout de fonctionnalités à des classes thread-safe existantes
        • 4.4.1 Verrouillage côté client
        • 4.4.2 Composition
      • 4.5 Documentation des politiques de synchronisation
        • 4.5.1 Interprétation des documentations vagues
    • 5.1 Collections synchronisées
      • 5.1.1 Problèmes avec les collections synchronisées
        • 5.1.2 Itérateurs et ConcurrentModificationException
        • 5.1.3 Itérateurs cachés
      • 5.2 Collections concurrentes
        • 5.2.1 ConcurrentHashMap
        • 5.2.2 Opérations atomiques supplémentaires sur les Map
        • 5.2.3 CopyOnWriteArrayList
      • 5.3 Files bloquantes et patron producteur-consommateur
        • 5.3.1 Exemple : indexation des disques
        • 5.3.2 Confinement en série
        • 5.3.3 Classe Deque et vol de tâches
      • 5.4 Méthodes bloquantes et interruptions
      • 5.5 Synchronisateurs
        • 5.5.1 Loquets
        • 5.5.2 FutureTask
        • 5.5.3 Sémaphores
        • 5.5.4 Barrières
      • 5.6 Construction d'un cache efficace et adaptable
      • 6.1 Exécution des tâches dans les threads
        • 6.1.1 Exécution séquentielle des tâches
        • 6.1.2 Création explicite de threads pour les tâches
        • 6.1.3 Inconvénients d'une création illimitée de threads
      • 6.2 Le framework Executor
        • 6.2.1 Exemple : serveur web utilisant Executor
        • 6.2.2 Politiques d'exécution
        • 6.2.3 Pools de threads
        • 6.2.4 Cycle de vie d'un Executor
        • 6.2.5 Tâches différées et périodiques
      • 6.3 Trouver un parallélisme exploitable
        • 6.3.1 Exemple : rendu séquentiel d'une page
        • 6.3.2 Tâches partielles : Callable et Future
        • 6.3.3 Exemple : affichage d'une page avec Future
        • 6.3.4 Limitations du parallélisme de tâches hétérogènes
        • 6.3.5 CompletionService : quand Executor rencontre BlockingQueue
        • 6.3.6 Exemple : affichage d'une page avec CompletionService
        • 6.3.7 Imposer des délais aux tâches
        • 6.3.8 Exemple : portail de réservations
      • Résumé
    • 7.1 Annulation des tâches
      • 7.1.1 Interruption
        • 7.1.2 Politiques d'interruption
        • 7.1.3 Répondre aux interruptions
        • 7.1.4 Exemple : exécution avec délai
        • 7.1.5 Annulation avec Future
        • 7.1.6 Méthodes bloquantes non interruptibles
        • 7.1.7 Encapsulation d'une annulation non standard avec newTaskFor()
      • 7.2 Arrêt d'un service reposant sur des threads
        • 7.2.1 Exemple : service de journalisation
        • 7.2.2 Méthodes d'arrêt de ExecutorService
        • 7.2.3 Pilules empoisonnées
        • 7.2.4 Exemple : un service d'exécution éphémère
        • 7.2.5 Limitations de shutdownNow()
      • 7.3 Gestion de la fin anormale d'un thread
        • 7.3.1 Gestionnaires d'exceptions non capturées
      • 7.4 Arrêt de la JVM
        • 7.4.1 Méthodes d'interception d'un ordre d'arrêt
        • 7.4.2 Threads démons
        • 7.4.3 Finaliseurs
      • Résumé
    • 8.1 Couplage implicite entre les tâches et les politiques d'exécution
      • 8.1.1 Interblocage par famine de thread
        • 8.1.2 Tâches longues
      • 8.2 Taille des pools de threads
      • 8.3 Configuration de ThreadPoolExecutor
        • 8.3.1 Création et suppression de threads
        • 8.3.2 Gestion des tâches en attente
        • 8.3.3 Politiques de saturation
        • 8.3.4 Fabriques de threads
        • 8.3.5 Personnalisation de ThreadPoolExecutor après sa construction
      • 8.4 Extension de ThreadPoolExecutor
        • 8.4.1 Exemple : ajout de statistiques à un pool de threads
      • 8.5 Parallélisation des algorithmes récursifs
        • 8.5.1 Exemple : un framework de jeu de réflexion
      • Résumé
    • 9.1 Pourquoi les interfaces graphiques sont-elles monothreads ?
      • 9.1.1 Traitement séquentiel des événements
        • 9.1.2 Confinement aux threads avec Swing
      • 9.2 Tâches courtes de l'interface graphique
      • 9.3 Tâches longues de l'interface graphique
        • 9.3.1 Annulation
        • 9.3.2 Indication de progression et de terminaison
        • 9.3.3 SwingWorker
      • 9.4 Modèles de données partagées
        • 9.4.1 Modèles de données thread-safe
        • 9.4.2 Modèles de données séparés
      • 9.5 Autres formes de sous-systèmes monothreads
      • Résumé
    • 10.1 Interblocages (deadlock)
      • 10.1.1 Interblocages liés à l'ordre du verrouillage
        • 10.1.2 Interblocages dynamiques liés à l'ordre du verrouillage
        • 10.1.3 Interblocages entre objets coopératifs
        • 10.1.4 Appels ouverts
        • 10.1.5 Interblocages liés aux ressources
      • 10.2 Éviter et diagnostiquer les interblocages
        • 10.2.1 Tentatives de verrouillage avec expiration
        • 10.2.2 Analyse des interblocages avec les traces des threads
      • 10.3 Autres problèmes de vivacité
        • 10.3.1 Famine
        • 10.3.2 Faible réactivité
        • 10.3.3 Livelock
      • Résumé
      • 11.1 Penser aux performances
        • 11.1.1 Performances et adaptabilité
        • 11.1.2 Compromis sur l'évaluation des performances
      • 11.2 La loi d'Amdahl
        • 11.2.1 Exemple : sérialisation cachée dans les frameworks
        • 11.2.2 Application qualitative de la loi d'Amdahl
      • 11.3 Coûts liés aux threads
        • 11.3.1 Changements de contexte
        • 11.3.2 Synchronisation de la mémoire
        • 11.3.3 Blocages
      • 11.4 Réduction de la compétition pour les verrous
        • 11.4.1 Réduction de la portée des verrous ("entrer, sortir")
        • 11.4.2 Réduire la granularité du verrouillage
        • 11.4.3 Découpage du verrouillage
        • 11.4.4 Éviter les points chauds
        • 11.4.5 Alternatives aux verrous exclusifs
        • 11.4.6 Surveillance de l'utilisation du processeur
        • 11.4.7 Dire non aux pools d'objets
      • 11.5 Exemple : comparaison des performances des Map
      • 11.6 Réduction du surcoût des changements de contexte
      • Résumé
    • 12.1 Tests de la justesse
      • 12.1.1 Tests unitaires de base
        • 12.1.2 Tests des opérations bloquantes
        • 12.1.3 Test de la sécurité vis-à-vis des threads
        • 12.1.4 Test de la gestion des ressources
        • 12.1.5 Utilisation des fonctions de rappel
        • 12.1.6 Production d'entrelacements supplémentaires
      • 12.2 Tests de performances
        • 12.2.1 Extension de PutTakeTest pour ajouter un timing
        • 12.2.2 Comparaison de plusieurs algorithmes
        • 12.2.3 Mesure de la réactivité
      • 12.3 Pièges des tests de performance
        • 12.3.1 Ramasse-miettes
        • 12.3.2 Compilation dynamique
        • 12.3.3 Échantillonnage irréaliste de portions de code
        • 12.3.4 Degrés de compétition irréalistes
        • 12.3.5 Élimination du code mort
      • 12.4 Approches de tests complémentaires
        • 12.4.1 Relecture du code
        • 12.4.2 Outils d'analyse statiques
        • 12.4.3 Techniques de tests orientées aspects
        • 12.4.4 Profileurs et outils de surveillance
      • Résumé
    • 13.1 Lock et ReentrantLock
      • 13.1.1 Prise de verrou scrutable et avec délai
        • 13.1.2 Prise de verrou interruptible
        • 13.1.3 Verrouillage non structuré en bloc
      • 13.2 Remarques sur les performances
      • 13.3 Équité
      • 13.4 synchronized vs. ReentrantLock
      • 13.5 Verrous en lecture-écriture
      • Résumé
    • 14.1 Gestion de la dépendance par rapport à l'état
      • 14.1.1 Exemple : propagation de l'échec de la précondition aux appelants
        • 14.1.2 Exemple : blocage brutal par essai et mise en sommeil
        • 14.1.3 Les files d'attente de condition
      • 14.2 Utilisation des files d'attente de condition
        • 14.2.1 Le prédicat de condition
        • 14.2.2 Réveil trop précoce
        • 14.2.3 Signaux manqués
        • 14.2.4 Notification
        • 14.2.5 Exemple : une classe "porte d'entrée"
        • 14.2.6 Problèmes de safety des sous-classes
        • 14.2.7 Encapsulation des files d'attente de condition
        • 14.2.8 Protocoles d'entrée et de sortie
      • 14.3 Objets conditions explicites
      • 14.4 Anatomie d'un synchronisateur
      • 14.5 AbstractQueuedSynchronizer
        • 14.5.1 Un loquet simple
      • 14.6 AQS dans les classes de java.util.concurrent
        • 14.6.1 ReentrantLock
        • 14.6.2 Semaphore et CountDownLatch
        • 14.6.3 FutureTask
        • 14.6.4 ReentrantReadWriteLock
      • Résumé
    • 15.1 Inconvénients du verrouillage
      • 15.2 Support matériel de la concurrence
        • 15.2.1 L'instruction Compare and swap
        • 15.2.2 Compteur non bloquant
        • 15.2.3 Support de CAS dans la JVM
      • 15.3 Classes de variables atomiques
        • 15.3.1 Variables atomiques comme "volatiles améliorées"
        • 15.3.2 Comparaison des performances des verrous et des variables atomiques
      • 15.4 Algorithmes non bloquants
        • 15.4.1 Pile non bloquante
        • 15.4.2 File chaînée non bloquante
        • 15.4.3 Modificateurs atomiques de champs
        • 15.4.4 Le problème ABA
      • Résumé
    • 16.1 Qu'est-ce qu'un modèle mémoire et pourquoi en a-t-on besoin ?
      • 16.1.1 Modèles mémoire des plates-formes
        • 16.1.2 Réorganisation
        • 16.1.3 Le modèle mémoire de Java en moins de cinq cents mots
        • 16.1.4 Tirer parti de la synchronisation
      • 16.2 Publication
        • 16.2.1 Publication incorrecte
        • 16.2.2 Publication correcte
        • 16.2.3 Idiomes de publication correcte
        • 16.2.4 Verrouillage contrôlé deux fois
      • 16.3 Initialisation sûre
      • Résumé
    • A.1 Annotations de classes
    • A.2 Annotations de champs et de méthodes
  • Bibliographie

Commentaires

Laisser un commentaire sur ce livre