Javase thread

Vers le Haut 2022-01-15 02:49:06 阅读数:969

javase thread

JavaSESenior Thread

Contenu principal

  • Multithreading

    • Points saillants,La conception et le développement Multi - Threading semblent un peu difficiles à comprendre.
    • Le Multithreading estJavaUne des techniques classiques de.Le Multithreading est la pierre angulaire de la future technologie de haute concurrence,C'est aussi la base.
  • Sécurité des fils

  • volatileMots clés

  • Atomicité

  • Et le contrat

  • Blocage

  • Pool de Threads

Objectifs pédagogiques

  • Dites le concept du processus

    • Un processus est un programme en cours d'exécution.
  • Dites le concept de thread

    • Le thread appartient au processus ,Un processus peut contenir plusieurs threads.
  • Être capable de comprendre la différence entre la concurrence et le parallélisme

    • Concurrence:C'est un tas de Threads pour préempterCPUExécutez - vous!!
    • Parallèle:Plusieurs Threads en même temps!!
  • Capable d'ouvrir de nouveaux fils

    • Appelezstart()Méthodes.
  • Capable de décrireJavaComment fonctionne le Multithreading moyen

    • Exécution simultanée,Aléatoire.
  • Possibilité de créer plusieurs Threads en utilisant des classes héritées

    a.Définir un héritage de classe de threadThreadCatégorie.
    b.RéécritureThreadClasserun()Méthodes
    c.Créer un objet de classe thread.
    d.Appeler l'objet thread classstart()Méthode Thread de démarrage
    
  • Possibilité de créer plusieurs Threads en implémentant des interfaces

    a.Définir une implémentation de classe de tâches threadRunnableInterface.Réécriturerun()Méthodes
    b.Créer un objet de tâche thread
    c.Envelopper l'objet thread Task en un objet thread
    -- public Thread(Runnable target)
    d.Appeler l'objet threadstart()Méthode Thread de démarrage.
    
  • Être capable de dire les avantages de la mise en œuvre de l'interface

    Après la mise en œuvre de l'interface, L'objet thread Task peut continuer à hériter d'autres classes ou à implémenter d'autres interfaces , Les fonctionnalités futures peuvent être étendues .
    Interface d'implémentation adaptée au pool de threads .
    Convient pour le partage des ressources
    
  • Capable de direvolatileRôle des mots clés

    • Implémenter la visibilité modifiée de plusieurs Threads pour les variables membres .
  • Peut expliquer volatileMots clés etsynchronizedLa différence entre les mots clés

    • volatileModifier les variables des membres, Implémenter la visibilité d'accès des variables , Mais il n'y a pas d'atomicité. .
    • synchronizedPeut modifier les méthodes et les blocs de code,Mécanisme de verrouillage, La sécurité du fil peut être réalisée . Les variables sont naturellement visibles après verrouillage .
  • Capable de comprendre le fonctionnement des classes atomiques

    • Basé surCASLa serrure optimiste.Sécurité assurée.
  • Capable de maîtriser les classes atomiques AtomicIntegerUtilisation de

    • private AtomicInteger atomicInteger = new AtomicInteger();
      int count = atomicInteger.incrementAndGet(); // Variable sous - jacente +1Et retour!
      
  • Capable de décrireConcurrentHashMapRôle de la classe

    • Haute sécurité,Thread Safe,Bonne performance globale.
  • Capable de décrireCountDownLatchRôle de la classe

  • Capable de décrireCyclicBarrierRôle de la classe

  • Capable d'exprimer SemaphoreRôle de la classe

  • Capable de décrireExchangerRôle de la classe

  • Capable de décrireJava Principe de fonctionnement du pool de plomb central

  • Capable de décrire la cause de l'impasse

No1Chapitre Multithreading

Nous étions,Le Programme d'apprentissage sans déclaration de saut,Tout est exécuté de haut en bas,Alors maintenant, je veux concevoir un programme,Jouer à des jeux et écouter des chansons,Comment concevoir?

Pour résoudre ces problèmes,Nous devons utiliser plusieurs processus ou fils pour résoudre.

1.1 Concurrence et parallélisme

  • Parallèle:Signifie que deux événements ou plus se produisentEn même tempsC'est arrivé.(Mise en œuvre simultanée).
  • Concurrence:Signifie que deux événements ou plus se produisentPendant la même périodeC'est arrivé.(Exécution alternée).

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-WNVFKQx9-1641479930000)(imgs/%E5%B9%B6%E8%A1%8C%E4%B8%8E%E5%B9%B6%E5%8F%91.bmp)]

​ Dans le système d'exploitation,Plusieurs programmes sont installés,La concurrence fait référence au fait qu'il y a plusieurs programmes qui fonctionnent simultanément sur une période de temps,C'est une seule. CPU Dans le système,Un seul programme peut être exécuté à chaque instant,C'est - à - dire qu'au niveau microscopique, ces programmes fonctionnent alternativement en temps partagé,Ça donne l'impression qu'il fonctionne en même temps,C'est parce que l'alternance de temps est très courte.

​ Et dans plusieurs CPU Dans le système,Ces programmes qui peuvent être exécutés simultanément peuvent être assignés à plusieurs processeurs(CPU),Réaliser l'exécution parallèle multitâche,C'est - à - dire utiliser chaque processeur pour traiter un programme qui peut être exécuté simultanément,De cette façon, plusieurs programmes peuvent être exécutés simultanément.Le marché actuel des ordinateurs dit Multi - Core CPU,C'est un processeur Multi - Core,Plus il y a de noyaux,Plus de programmes sont traités en parallèle,Peut grandement améliorer l'efficacité du fonctionnement de l'ordinateur.

Attention!:L'ordinateur d'un processeur à noyau unique ne peut certainement pas traiter plusieurs tâches en parallèle,Il ne peut y avoir que plusieurs tâches en une seuleCPUExécution simultanée.Même chose.,Les fils sont pareils,Comprendre macroscopiquement que les fils fonctionnent en parallèle,Mais l'analyse microscopique fonctionne en série,C'est - à - dire qu'un thread tourne un thread,Quand il n'y a qu'un seul systèmeCPUHeure,Les Threads exécutent plusieurs Threads dans un certain ordre,On appelle ça la programmation par fil.

1.2 Fils et processus

  • Processus:Est une application qui fonctionne en mémoire,Chaque processus a un espace mémoire indépendant,Une application peut exécuter plusieurs processus en même temps;Un processus est aussi un processus d'exécution d'un programme,Est l'unit é de base du programme de fonctionnement du système;Le système exécute un programme qui est un processus à partir de la création、Le processus d'extinction.
  • Thread: Est une unit é d'exécution dans le processus ,Responsable de l'exécution du programme dans le processus actuel,Il y a au moins un thread dans un processus.Il peut y avoir plusieurs Threads dans un processus,.Cette application peut aussi être appelée Multithreading.

Processus

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-RAmyOD7F-1641479930005)(imgs/%E8%BF%9B%E7%A8%8B%E6%A6%82%E5%BF%B5.png)]

Thread

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-lW1LETmk-1641479930017)(imgs/%E7%BA%BF%E7%A8%8B%E6%A6%82%E5%BF%B5.png)]

Différence entre un processus et un thread

  • Processus:Il y a un espace mémoire séparé,Stockage des données dans le processus(Espace de tas et espace de pile)Est indépendant,Au moins un thread.
  • Thread:L'espace de tas est partagé,L'espace de pile est indépendant,Le thread consomme beaucoup moins de ressources que le processus.

**Attention!:** Voici les points de connaissance

1: Parce que plusieurs Threads dans un processus fonctionnent simultanément , Il y a donc un ordre microscopique , Quel thread exécute dépend entièrement de CPU Répartition, Les programmeurs ne peuvent pas interférer . Et cela crée le caractère aléatoire du Multithreading .

2:Java Le processus du programme contient au moins deux fils , Le processus principal est main() Méthode thread , L'autre est le thread du mécanisme de collecte des ordures .Chaque fois que java Quand une commande exécute une classe ,En fait, ça déclenche un JVM,Chaque JVM En fait, un thread a été lancé dans le système d'exploitation ,java Il dispose d'un mécanisme de collecte des ordures ,Donc, dans Java L'exécution démarrera au moins deux Threads .

3: Parce que créer un thread coûte beaucoup moins cher que créer un processus , Alors, pendant que nous développons des opérations multitâches , La création de multithreads est généralement envisagée , Au lieu de créer plusieurs processus .

Programmation des threads:

  • Répartition du temps

    ​ Tous les fils sont utilisés à tour de rôle CPU Droit d'utiliser,Répartir également la consommation par fil CPU Le temps.

  • Calendrier préventif

    ​ Priorité aux Threads hautement prioritaires CPU,Si le thread a la même priorité,Alors, choisissez au hasard(Randomisation des fils),JavaUtilisé pour l'ordonnancement préventif.

1.3 ThreadCatégorie

Le thread est activé et nous avons besoin dejava.lang.ThreadCatégorie,APICertaines méthodes de Threading sont définies dans cette classe,Les détails sont les suivants:

Méthode de construction:

  • public Thread():Assigner un nouvel objet thread.
  • public Thread(String name):Assigner un nouvel objet thread avec le nom spécifié.
  • public Thread(Runnable target):Assigner un nouvel objet thread avec la cible spécifiée.
  • public Thread(Runnable target,String name):Assigner un nouvel objet thread avec la cible spécifiée et spécifier un nom.

Méthodes courantes:

  • public String getName():Obtenir le nom actuel du thread.
  • public void start():Provoque l'exécution de ce thread; JavaLa machine virtuelle appelle ce threadrunMéthodes.
  • public void run():La tâche à accomplir par ce thread définit le Code ici.
  • public static void sleep(long millis):.Mettre en pause le thread actuellement en cours d'exécution dans le nombre spécifié de millisecondes(Suspension temporaire de l'exécution).
  • public static Thread currentThread():Renvoie une référence à l'objet thread actuellement en cours d'exécution.

FeuilleterAPI Il y a deux façons de créer un thread ,L'un est l'héritageThreadMode classe,L'un est la réalisationRunnableMode d'interface, Mode 1 Nous avons terminé la veille , Ensuite, expliquez comment le mode 2 est mis en œuvre .

1.4 Créer un thread comme un__Mode d'héritage

​ JavaUtiliserjava.lang.ThreadLa classe représenteThread,Tous les objets filetés doivent êtreThreadExemples de classes ou de sous - classes.Le rôle de chaque thread est d'accomplir certaines tâches,En fait, c'est exécuter un flux de programme, c'est - à - dire un code exécuté séquentiellement.JavaUtilisez l'exécuteur de fil pour représenter ce flux de programme.JavaPar héritageThreadAllez.CréationEtDémarrer MultithreadingLes étapes sont les suivantes::

  1. DéfinitionThreadUne sous - classe de la classe,Et réécrire cette classerun()Méthodes,Lerun()Le corps de la méthode représente la tâche que le thread doit accomplir,Et donc,run()La méthode s'appelle thread Executor.
  2. CréationThreadExemples de sous - classes,L'objet thread est créé
  3. Appeler l'objet threadstart()Méthode pour démarrer le thread

Les codes sont les suivants::

Catégorie d'essai:

public class Demo01 {

public static void main(String[] args) {

//Créer un objet thread personnalisé
MyThread mt = new MyThread("Nouveau thread!");
//Ouvrir un nouveau thread
mt.start();
//Exécuter dans la méthode principaleforCycle
for (int i = 0; i < 200; i++) {

System.out.println("mainThread!"+i);
}
}
}

Classe de thread personnalisée:

public class MyThread extends Thread {

//Définit la méthode de construction pour spécifier le nom du fil
public MyThread(String name) {

//Appelé parentStringMéthode de construction des paramètres,Spécifier le nom du thread
super(name);
}
public MyThread() {

// Ne spécifiez pas le nom du thread , Le thread a un nom par défaut Thread-0
}
/** * RéécriturerunMéthodes,Complétez la logique que ce thread exécute */
@Override
public void run() {

for (int i = 0; i < 200; i++) {

System.out.println(getName()+":Mise en œuvre en cours!"+i);
}
}
}

1.5 Comment créer un thread II__Mode de réalisation

Adoptionjava.lang.RunnableC'est aussi très courant,On a juste besoin de réécrire.runLa méthode est juste.

Les étapes sont les suivantes:

  1. DéfinitionRunnableClasse d'implémentation de l'interface,Et réécrire l'interfacerun()Méthodes,Lerun()Le corps de la méthode est également l'exécuteur du thread pour ce thread.
  2. CréationRunnableExemples de classes d'implémentation,Et par exemple,ThreadDetargetPour créerThreadObjet,LeThreadL'objet est le vrai objet thread.
  3. Appeler l'objet threadstart()Comment démarrer le thread.

Les codes sont les suivants::

public class MyRunnable implements Runnable{

@Override
public void run() {

for (int i = 0; i < 20; i++) {

System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class Demo {

public static void main(String[] args) {

//Créer un objet de classe personnalisé Objet de tâche thread
MyRunnable mr = new MyRunnable();
//Créer un objet thread
Thread t = new Thread(mr, "Xiao Qiang");
t.start();
for (int i = 0; i < 20; i++) {

System.out.println("Wangcai. " + i);
}
}
}

​ Par la réalisationRunnableInterface,De sorte que la classe a les caractéristiques d'une classe multithreadée.run()La méthode est une cible d'exécution d'un programme multithreadé.Tout le Code multithreadé est dansrunDans la méthode.ThreadLa classe est également implémentéeRunnableClasse d'interface.

​ Au démarrage du Multithreading,Il faut d'abord passerThreadMéthode de construction de la classeThread(Runnable target) Construire des objets,Puis appelezThreadObjetstart()Méthode pour exécuter le Code multithreadé.

​ En fait, tout le Code multithreadé est exécuté parThreadDestart()La méthode à utiliser.Donc,,Que ce soit par héritageThreadClasse ou implémentationRunnableInterface pour implémenter le Multithreading,Finalement, c'est passé.ThreadObjet deAPIPour contrôler les fils,FamiliarisationThreadClasseAPIEst la base de la programmation multithreadée.

tips:RunnableObjet uniquement en tant queThreadObjettarget,RunnableInclus dans la classe d'implémentationrun()Méthode en tant qu'exécuteur de fil seulement.Et l'objet thread réel est toujoursThreadExemple,C'est justeThreadLe thread est responsable de l'exécution de sontargetDerun()Méthodes.

ThreadEtRunnableLa différence entre

Résumé:

RéalisationRunnableInterface plus que l'héritageThreadAvantages de la classe:

  1. Convient à plusieurs Threads du même Code de programme pour partager la même ressource.
  2. Peut être évitéjavaLimites de l'héritage unique dans.
  3. Augmenter la robustesse du programme,Réaliser l'opération de découplage,Le Code peut être partagé par plusieurs Threads,Le Code et le fil sont indépendants.
  4. Le pool de Threads ne peut être placé que dans l'implémentationRunableOuCallableThreads de classe.

1.6 Méthode de classe interne anonyme

Utiliser la méthode de classe interne anonyme du thread,Il est facile d'implémenter chaque thread pour effectuer différentes tâches de thread.

Implémenter en utilisant des classes internes anonymesRunnableInterface,ReRunnableDans l'interfacerunMéthodes:

public class NoNameInnerClassThread {

public static void main(String[] args) {

// new Runnable(){

// public void run(){

// for (int i = 0; i < 20; i++) {

// System.out.println("Zhang Yu:"+i);
// }
// } 
// }; //---Tout ça. équivalent ànew MyRunnable()
Runnable r = new Runnable(){

public void run(){

for (int i = 0; i < 20; i++) {

System.out.println("Zhang Yu:"+i);
}
}
};
new Thread(r).start();
for (int i = 0; i < 20; i++) {

System.out.println("Fei Yuqing:"+i);
}
}
}

No2Chapitre Sécurité des fils

2.1 Sécurité des fils

Si plus d'un thread fonctionne en même temps,Et ces Threads peuvent exécuter ce code en même temps.Le résultat de chaque exécution du programme est le même que celui d'un seul thread,Et d'autres variables ont la même valeur que prévu,C'est sans fil..

Nous avons adopté un cas , Démontrer les problèmes de sécurité du thread :

​ Le cinéma vend des billets. , Nous simulons le processus de vente de billets dans les cinémas . Supposons que le film à jouer soit “ Huluwa vs Otman ”, Total des sièges pour ce film 100- Oui.( Ce film ne peut être vendu que 100Un billet.). Simulons la fenêtre de billetterie du cinéma. , Réaliser la vente simultanée de plusieurs fenêtres “ Huluwa vs Otman ” Ce billet de film ( Plusieurs fenêtres ensemble pour vendre ceci 100Un billet.), Fenêtre requise ,Utiliser des objets filetés pour simuler;J'ai besoin de billets,RunnableSous - classe interface pour simuler.

Billet analogique :

public class Ticket implements Runnable {

private int ticket = 100;
/* * Effectuer la vente de billets */
@Override
public void run() {

//Opération de vente de billets par fenêtre 
//Fenêtre Ouvert pour toujours 
while (true) {

if (ticket > 0) {
//Il y a des billets. Peut être vendu
// Opérations de billetterie 
//UtilisersleepSimuler l'heure du billet 
try {

Thread.sleep(100);
} catch (InterruptedException e) {

// TODO Auto-generated catch block
e.printStackTrace();
}
//Obtient le nom de l'objet thread actuel 
String name = Thread.currentThread().getName();
System.out.println(name + "En vente:" + ticket--);
}
}
}
}

Catégorie d'essai:

public class Demo {

public static void main(String[] args) {

//Créer un objet de tâche thread
Ticket ticket = new Ticket();
//Créer trois objets de fenêtre
Thread t1 = new Thread(ticket, "Fenêtre1");
Thread t2 = new Thread(ticket, "Fenêtre2");
Thread t3 = new Thread(ticket, "Fenêtre3");
//Vendre des billets en même temps
t1.start();
t2.start();
t3.start();
}
}

Une partie du résultat est que :

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-nUZvOniK-1641479930025)(imgs/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98.png)]

Il y a eu deux problèmes avec le programme Discovery :

  1. Nombre égal de voix ,Par exemple,5 Ce billet a été vendu deux fois. .
  2. Billets inexistants ,Par exemple,0 Votes et -1Votes,N'existe pas.

Ce genre de problème,Quelques fenêtres(Thread)Les votes ne sont pas synchronisés,Ce problème est appelé thread Unsafe.

Les problèmes de sécurité des fils sont causés par des variables globales et statiques.Si la variable globale dans chaque thread、Les variables statiques ne peuvent être lues que,Sans écrire,En général,Cette Variable globale est sans fil;S'il y a plus d'un thread qui écrit en même temps,En général, la synchronisation des fils doit être envisagée,Sinon, cela pourrait affecter la sécurité du fil.

2.2 Synchronisation des fils

La synchronisation des Threads vise à résoudre les problèmes de sécurité des Threads .

Lorsque nous utilisons plusieurs Threads pour accéder à la même ressource,Et il y a des opérations d'écriture sur la ressource dans plusieurs Threads,Il y a des problèmes de sécurité de fil.

Pour résoudre le problème de sécurité de l'accès simultané multithreadé susmentionné à une ressource:C'est - à - dire résoudre le problème des votes répétés et des votes inexistants,JavaLe mécanisme de synchronisation est fourni dans(synchronized)C'est réglé..

D & apos; après le résumé de l & apos; affaire :

Fenêtre1Quand le thread entre en action,Fenêtre2Et fenêtres3Les fils ne peuvent attendre qu'à l'extérieur,Fenêtre1Opération terminée,Fenêtre1Et fenêtres2Et fenêtres3 Pour avoir accès au Code pour exécuter .C'est - à - dire quand un thread modifie une ressource partagée,Un autre thread ne peut pas modifier la ressource,Attendez que les modifications soient synchronisées,Pour volerCPURessources,Terminer l'opération correspondante,La synchronisation des données est garantie,Correction de l'insécurité des fils.

Pour s'assurer que chaque fil fonctionne correctement avec l'atome,JavaLe mécanisme de synchronisation des fils a été introduit.

Comment l'utiliser? ?

Il y a trois façons de faire la synchronisation:

  1. Bloc de code de synchronisation.
  2. Méthode de synchronisation.
  3. Mécanisme de verrouillage.

2.3 Bloc de code de synchronisation

  • Bloc de code de synchronisationsynchronizedLes mots clés peuvent être utilisés dans un bloc d'une méthode,Indique un accès exclusif aux ressources de ce bloc seulement.

Format:

synchronized(Verrouillage synchrone){

Code nécessitant une opération de synchronisation
}

Verrouillage synchrone:

Le verrouillage synchrone d'un objet n'est qu'un concept,On peut imaginer qu'une serrure soit marquée sur un objet.

  1. Objet de verrouillage Peut être n'importe quel type.
  2. Plusieurs objets filetés Pour utiliser la même serrure.

Attention!:À tout moment,Autoriser jusqu'à un thread à avoir un verrou de synchronisation,Celui qui a la serrure entre dans le bloc de code,Les autres fils ne peuvent attendre qu'à l'extérieur(BLOCKED).

Résoudre le Code en utilisant le bloc de synchronisation :

public class Ticket implements Runnable{

private int ticket = 100;
Object lock = new Object();
/* * Effectuer la vente de billets */
@Override
public void run() {

//Opération de vente de billets par fenêtre 
//Fenêtre Ouvert pour toujours 
while(true){

synchronized (lock) {

if(ticket>0){
//Il y a des billets. Peut être vendu
// Opérations de billetterie 
//UtilisersleepSimuler l'heure du billet 
try {

Thread.sleep(50);
} catch (InterruptedException e) {

// TODO Auto-generated catch block
e.printStackTrace();
}
//Obtient le nom de l'objet thread actuel 
String name = Thread.currentThread().getName();
System.out.println(name+"En vente:"+ticket--);
}
}
}
}
}

Lorsque le bloc de synchronisation est utilisé , Problèmes de sécurité avec les fils mentionnés ci - dessus ,C'est réglé..

2.4 Méthode de synchronisation

  • Méthode de synchronisation:UtilisersynchronizedMéthode de modification,C'est ce qu'on appelle la méthode de synchronisation,GarantieAQuand le thread exécute la méthode,Les autres fils ne peuvent attendre qu'en dehors de la méthode.

Format:

public synchronized void method(){

Code qui peut causer des problèmes de sécurité de fil
}

Qui est la serrure de synchronisation? ?

​ Pour nonstaticMéthodes,La serrure de synchronisation estthis.

​ PourstaticMéthodes, Nous utilisons l'objet Bytecode de la classe dans laquelle se trouve la méthode courante (Nom de la classe.class).

Utilisez le Code de méthode de synchronisation ci - dessous :

public class Ticket implements Runnable{

private int ticket = 100;
/* * Effectuer la vente de billets */
@Override
public void run() {

//Opération de vente de billets par fenêtre 
//Fenêtre Ouvert pour toujours 
while(true){

sellTicket();
}
}
/* * Objet de verrouillage - Oui. Qui appelle cette méthode? C'est qui * Implicite Objet de verrouillage C'est this * */
public synchronized void sellTicket(){

if(ticket>0){
//Il y a des billets. Peut être vendu 
// Opérations de billetterie 
//UtilisersleepSimuler l'heure du billet 
try {

Thread.sleep(100);
} catch (InterruptedException e) {

// TODO Auto-generated catch block
e.printStackTrace();
}
//Obtient le nom de l'objet thread actuel 
String name = Thread.currentThread().getName();
System.out.println(name+"En vente:"+ticket--);
}
}
}

2.5 LockVerrouillage

java.util.concurrent.locks.LockLe mécanisme offre plus quesynchronizedBloc de code etsynchronizedMéthode opération de verrouillage plus large,Bloc de code de synchronisation/Fonction de la méthode de synchronisationLockTous.,Plus puissant que ça

LockLes serrures sont également appelées serrures synchrones,Le verrouillage et le déverrouillage ont été programmés,Comme suit:

  • public void lock():Plus de serrures de synchronisation.
  • public void unlock():Relâchez la serrure de synchronisation.

Utiliser comme suit::

public class Ticket implements Runnable{

private int ticket = 100;
Lock lock = new ReentrantLock();
/* * Effectuer la vente de billets */
@Override
public void run() {

//Opération de vente de billets par fenêtre 
//Fenêtre Ouvert pour toujours 
while(true){

lock.lock();
if(ticket>0){
//Il y a des billets. Peut être vendu
// Opérations de billetterie 
//UtilisersleepSimuler l'heure du billet 
try {

Thread.sleep(50);
} catch (InterruptedException e) {

// TODO Auto-generated catch block
e.printStackTrace();
}
//Obtient le nom de l'objet thread actuel 
String name = Thread.currentThread().getName();
System.out.println(name+"En vente:"+ticket--);
}
lock.unlock();
}
}
}

No3Chapitre État du fil

3.1 Aperçu de l'état du fil

Le processus complet de la naissance à la mort du fil :

Une fois le thread créé et lancé,Il n'est pas entré dans l'état d'exécution au démarrage,Et pas toujours en état d'exécution.Dans le cycle de vie du fil,Il y a plusieurs états?InAPIMoyennejava.lang.Thread.StateSix états de thread sont donnés dans cette énumération:

Voici une liste des conditions dans lesquelles chaque état de fil se produit , Chaque état sera analysé en détail ci - dessous

État du fil Conditions qui ont conduit à l'état
NEW(Nouveau) Le thread vient d'être créé,Mais ça n'a pas commencé.Pas encore appeléstartMéthodes.MyThread t = new MyThread Objets filetés seulement , Pas de caractéristiques de fil .
Runnable(Opérationnel) Le thread peut êtrejavaÉtat de fonctionnement de la machine virtuelle,Peut - être qu'il exécute son propre code,Ou peut - être pas,Cela dépend du processeur os.Appelét.start()Méthodes :Prêt.( Nom classique )
Blocked(Verrouillage bloqué) Quand un thread essaie d'obtenir un verrouillage d'objet,Et la serrure de l'objet est tenue par un autre thread,Le thread entreBlockedStatut;Quand le fil tient la serrure,Ce thread va devenirRunnableStatut.
Waiting(Une attente infinie) Un thread attend qu'un autre thread exécute un(Réveillez - vous.)En action,Le thread entre dansWaitingStatut.Il n'y a pas de réveil automatique après l'entrée dans cet état,Vous devez attendre qu'un autre thread appellenotifyOunotifyAllLa méthode peut réveiller.
Timed Waiting(Temps d'attente) Même chose.waitingStatut,Plusieurs méthodes ont des paramètres de temporisation,Appelez - les et entrezTimed WaitingStatut.Cet état sera maintenu jusqu'à l'expiration du délai ou jusqu'à ce qu'un avis de réveil soit reçu.Les méthodes courantes avec paramètres de temporisation sont les suivantes:Thread.sleep 、Object.wait.
Teminated(Résiliation) Parce querunMéthode sortie normale et mort,Ou parce qu'il n'y a pas d'exception capturéerunMéthode et mort.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-P0509WBz-1641479930035)(imgs/1573352376549.png)]

​ Nous n'avons pas besoin d'étudier les principes de mise en oeuvre de ces états , .Il suffit de savoir qu'il y a un tel état dans le Threading . Comment comprendre ces états , Il est facile de comprendre la création et la résiliation , Nous allons juste examiner les fils de Runnable(Opérationnel) Problèmes de transition entre l'état et l'état non opérationnel .

3.2 SommeilsleepMéthodes

Nous voyons qu'il y a un état dans l'état appelé temps d'attente ,Peut passerThread Méthode de classe pour démontrer .

public static void sleep(long time) Laissez le thread actuel dormir , Se réveiller automatiquement en millisecondes pour continuer

public class Test{

public static void main(String[] args){

for(int i = 1;i<=5;i++){

Thread.sleep(1000);
System.out.println(i)
}
}
}

Puis nous avons découvert que le thread principal exécutait sleep La méthode dort 1Continuez dans quelques secondes..

3.3 Attendre et se réveiller

ObjectMéthode de la classe

public void wait() : Mettre le thread actuel en attente Cette méthode doit verrouiller l'appel de l'objet.

public class Demo1_wait {

public static void main(String[] args) throws InterruptedException {

// Étapes1 : Sous - thread on, Entrer dans un état d'attente infini , Non réveillé ,Impossible de continuer.
new Thread(() -> {

try {

System.out.println("begin wait ....");
synchronized ("") {

"".wait();
}
System.out.println("over");
} catch (Exception e) {

}
}).start();
}

public void notify() : Réveille le thread en attente sur l'objet de verrouillage actuel Cette méthode doit verrouiller l'appel de l'objet.

public class Demo2_notify {

public static void main(String[] args) throws InterruptedException {

// Étapes1 : Sous - thread on, Entrer dans un état d'attente infini , Non réveillé ,Impossible de continuer.
new Thread(() -> {

try {

System.out.println("begin wait ....");
synchronized ("") {

"".wait();
}
System.out.println("over");
} catch (Exception e) {

}
}).start();
//Étapes2: Après avoir ajouté le code suivant , 3Dans quelques secondes.,Sera exécuténotifyMéthodes, Réveillez - vous.waitThread Central.
Thread.sleep(3000);
new Thread(() -> {

try {

synchronized ("") {

System.out.println("Réveillez - vous.");
"".notify();
}
} catch (Exception e) {

}
}).start();
}
}

3.4 Attendre le cas de réveil ( Le magasin de pains vend des pains. )

Définir une collection, Le fil de pavage du paquet complète le paquet de production , Les paquets sont ajoutés à la collection ; Le fil d'alimentation complète le paquet d'achat , Les enveloppes sont retirées de la collection .
1. Quand il n'y a pas de pain(L'état du paquet estfalse),Le fil d'alimentation attend.
2. Fils de pavage de paquets production de paquets(C'est - à - dire que l'état du paquet esttrue),Et prévenez le fil mangeur(Désactiver l'état d'attente de la nourriture)

Exemple de code:

Générer des sous - classes de paquets :

public class BaoZiPu extends Thread{

private List<String> list ;
public BaoZiPu(String name,ArrayList<String> list){

super(name);
this.list = list;
}
@Override
public void run() {

int i = 0;
while(true){

//listComme objet de verrouillage
synchronized (list){

if(list.size()>0){

// Le thread qui stocke l'élément entre dans l'état d'attente 
try {

list.wait();
} catch (InterruptedException e) {

e.printStackTrace();
}
}
// Si le thread n'est pas entré dans l'état d'attente Description il n'y a pas d'éléments dans la collection 
//Ajouter un élément à la collection
list.add("Baozi."+i++);
System.out.println(list);
// Il y a déjà des éléments dans la collection Réveiller le fil pour obtenir l'élément
list.notify();
}
}
}
}

Sous - catégories de produits de consommation :

public class ChiHuo extends Thread {

private List<String> list ;
public ChiHuo(String name,ArrayList<String> list){

super(name);
this.list = list;
}
@Override
public void run() {

// Pour voir l'effet Écrivez un cycle mort
while(true){

// Parce que la même collection est utilisée listComme objet de verrouillage
synchronized (list){

//S'il n'y a pas d'éléments dans la collection, Le thread qui obtient l'élément entre dans l'état d'attente 
if(list.size()==0){

try {

list.wait();
} catch (InterruptedException e) {

e.printStackTrace();
}
}
// S'il y a un élément dans la collection Le thread qui obtient l'élément obtient l'élément (Supprimer)
list.remove(0);
//Imprimer la collection Il n'y a plus d'éléments dans la collection 
System.out.println(list);
// Il n'y a plus d'éléments dans la collection Réveiller le thread qui a ajouté l'élément Ajouter un élément à la collection
list.notify();
}
}
}
}

Catégorie d'essai:

public class Demo {

public static void main(String[] args) {

// Attendre le cas de réveil 
List<String> list = new ArrayList<>();
// Créer un objet thread 
BaoZiPu bzp = new BaoZiPu("Baozi shop",list);
ChiHuo ch = new ChiHuo("Mangez.",list);
// Activer le thread
bzp.start();
ch.start();
}
}

No4Chapitre Mode pool de threads

4.1 L'idée d'un pool de threads

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-Iezdz8Zx-1641479930039)(imgs/Piscine.jpg)]

Nous créons un thread quand nous l'utilisons,C'est très facile à réaliser,Mais il y aura un problème:

Si le nombre de Threads simultanés est élevé,Et chaque thread exécute une tâche de courte durée qui se termine,Cela réduit considérablement l'efficacité du système en créant fréquemment des fils,Parce que la création et la destruction fréquentes de Threads prennent du temps, Les Threads sont également des ressources système précieuses .

Y a - t - il un moyen de réutiliser les fils,Est d'accomplir une tâche,Ne pas détruire,Au lieu de cela, vous pouvez continuer à accomplir d'autres tâches?

InJavaEn passant parPool de Threads Pour obtenir cet effet .Aujourd'hui, nous allons expliquer en détailJavaPool de Threads pour.

Un. .Introduction
L'utilisation de Threads dans java La Chine occupe une place extrêmement importante ,Injdk1.4 Avant jdkDans la version, L'utilisation du pool de Threads est extrêmement rudimentaire .Injdk1.5 Après ça, la situation s'est beaucoup améliorée .Jdk1.5Plus tard, j'ai ajoutéjava.util.concurrentSac, Le contenu principal de ce paquet java Utilisation de Threads et de pools de Threads . Cela nous a beaucoup aidés à résoudre les problèmes de filetage dans le développement .

2..Pool de Threads
1.Le rôle du pool de Threads:Le but du pool de Threads est de limiter le nombre de Threads d'exécution dans le système.
Selon l'environnement du système , Le nombre de fils peut être réglé automatiquement ou manuellement , Pour un fonctionnement optimal ;
Moins de gaspillage des ressources du système , Trop de systèmes encombrés et inefficaces .
Contrôler le nombre de Threads avec le pool de threads , Autres files d'attente .

Une mission terminée., Commencez par la tâche la plus ancienne de la file d'attente . S'il n'y a pas de tâches en attente dans la file d'attente , Cette ressource du pool de Threads est en attente . Quand une nouvelle tâche doit être exécutée ,S'il y a des fils de travail en attente dans le pool de fils,Ça va commencer.;Sinon, entrez dans la file d'attente.

2.Pourquoi utiliser un pool de threads:
1.Réduit le nombre de fois que vous créez et détruisez des Threads,Chaque fil de travail peut être réutilisé,Exécuter plusieurs tâches.
2.Selon la capacité du système,Ajuster le nombre de fils de travail dans le pool de fils, Prévenir la consommation excessive de mémoire ,Et j'ai mis le serveur à terre.(Chaque fil nécessite environ1MBMémoire,Plus les fils sont ouverts,Plus la mémoire est consommée,Dernier arrêt).

JavaL'interface de haut niveau du pool de Threads à l'intérieur estExecutors,Au sens strict du termeExecutorsCe n'est pas un pool de Threads,Et juste un outil pour exécuter le thread.Véritable interface de pool de threadsExecutorService.

3. Quelques classes importantes :
ExecutorService Véritable interface de pool de threads.
ScheduledExecutorService Oui.Timer/TimerTaskSimilaire, Résoudre les problèmes qui nécessitent la répétition des tâches .
ThreadPoolExecutor ExecutorServiceImplémentation par défaut pour.
ScheduledThreadPoolExecutor SuccessionThreadPoolExecutorDeScheduledExecutorServiceMise en œuvre de l'interface, Mise en oeuvre en classe de l'ordonnancement périodique des tâches .

Il est plus compliqué de configurer un pool de Threads,Surtout si le principe du pool de Threads n'est pas très clair,Il est très probable que le pool de Threads configuré n'est pas meilleur,Donc, dansExecutorsIl y a des usines statiques dans la classe,Générer des pools de Threads communs.

  1. newSingleThreadExecutor
    Créer un pool de Threads à un seul thread.Ce pool de Threads n'a qu'un seul thread qui fonctionne,C'est - à - dire qu'un seul thread exécute toutes les tâches en série.Si ce fil unique se termine par une exception,Alors il y aura un nouveau thread pour le remplacer.Ce pool de Threads garantit que toutes les tâches sont exécutées dans l'ordre dans lequel elles sont engagées.
  2. newFixedThreadPool
    Créer un pool de Threads de taille fixe.
    Créer un thread à chaque fois qu'une tâche est soumise,Jusqu'à ce que le thread atteigne la taille maximale du pool de Threads.
    La taille du pool de Threads reste la même une fois qu'elle atteint son maximum,Si un thread se termine par une exception d'exécution,
    Alors le pool de Threads sera complété par un nouveau thread.
  3. newCachedThreadPool
    Créer un pool de Threads cache.
    Si la taille du pool de Threads dépasse celle requise pour traiter la tâche,
    Alors une partie du ralenti sera recyclée (60Les secondes n'exécutent pas la tâche)Les fils de,Quand le nombre de tâches augmente,
    Ce pool de Threads peut ajouter intelligemment de nouveaux Threads pour gérer les tâches.Ce pool de Threads ne limite pas la taille du pool de Threads,
    La taille du pool de Threads dépend entièrement du système d'exploitation(Ou plutôtJVM)Taille maximale du fil qui peut être créé.
  4. newScheduledThreadPool
    Créer un pool de Threads de taille illimitée.Ce pool de Threads prend en charge les exigences d'exécution des tâches programmées et cycliques.

4.2 Concept de pool de Threads

  • **Pool de Threads:**En fait, c'est un conteneur qui contient plusieurs fils,Les fils peuvent être réutilisés,Suppression de la création fréquente d'objets filetés,Il n'est pas nécessaire de créer des Threads à plusieurs reprises et de consommer trop de ressources.

Parce qu'il y a beaucoup d'opérations dans le pool de Threads qui sont liées à l'optimisation des ressources , Nous n'en parlerons pas ici. . Nous utilisons un diagramme pour comprendre comment fonctionne le pool de threads :

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-ilH2oswB-1641479930042)(imgs/Principe du pool de Threads.bmp)]

L'utilisation rationnelle des pools de Threads offre trois avantages:

  1. Réduire la consommation de ressources.Réduit le nombre de fois que vous créez et détruisez des Threads,Chaque fil de travail peut être réutilisé,Exécuter plusieurs tâches.
  2. Augmenter la vitesse de réponse.Quand la mission arrive,Les tâches peuvent être exécutées sans attendre la création du thread.
  3. Améliorer la maniabilité des fils.Selon la capacité du système,Ajuster le nombre de fils de travail dans le pool de fils, Prévenir la consommation excessive de mémoire ,Et j'ai mis le serveur à terre.(Chaque fil nécessite environ1MBMémoire,Plus les fils sont ouverts,Plus la mémoire est consommée,Dernier arrêt).

4.3 Utilisation du pool de threads

JavaL'interface de haut niveau du pool de Threads à l'intérieur estjava.util.concurrent.Executor,Mais techniquementExecutorCe n'est pas un pool de Threads,Et juste un outil pour exécuter le thread.La vraie interface threadpool estjava.util.concurrent.ExecutorService.

Il est plus compliqué de configurer un pool de Threads,Surtout si le principe du pool de Threads n'est pas très clair,Il est très probable que le pool de Threads configuré n'est pas meilleur,Donc, dansjava.util.concurrent.ExecutorsLa classe thread Factory fournit quelques usines statiques,Générer des pools de Threads communs.Utilisation officielle recommandéeExecutorsClasses d'ingénierie pour créer des objets threadpool.

ExecutorsIl y a des méthodes dans la classe pour créer un pool de Threads comme suit:

  • public static ExecutorService newFixedThreadPool(int nThreads):Renvoie l'objet threadpool.( Créer un pool délimité ,C'est - à - dire que le nombre maximum de Threads dans le pool peut être spécifié)

J'ai un pool de ThreadsExecutorService Objet,Comment l'utiliser?,Une façon d'utiliser l'objet threadpool est définie ici comme suit:

  • public Future<?> submit(Runnable task):Obtenir un objet thread dans le pool thread,Et la mise en œuvre

    FutureInterface:Utilisé pour enregistrer les résultats après l'exécution de la tâche thread.

Pour travailler avec des objets thread dans un pool thread:

  1. Créer un objet threadpool.
  2. CréationRunnableObjet de la Sous - classe interface.(task)
  3. SoumettreRunnableObjet de la Sous - classe interface.(take task)
  4. Fermer le pool de Threads(Pas en général.).

RunnableCode de classe d'implémentation:

public class MyRunnable implements Runnable {

@Override
public void run() {

System.out.println(" Je veux un coach. ");
try {

Thread.sleep(2000);
} catch (InterruptedException e) {

e.printStackTrace();
}
System.out.println("Le Coach arrive: " + Thread.currentThread().getName());
System.out.println(" Apprends - moi à nager. , Après la livraison , Le coach est de retour à la piscine. ");
}
}

Classe de test de pool de threads :

public class ThreadPoolDemo {

public static void main(String[] args) {

// Créer un objet threadpool
ExecutorService service = Executors.newFixedThreadPool(2);//Contient2Objets filetés
// CréationRunnableObjet instance
MyRunnable r = new MyRunnable();
// Comment créer votre propre objet thread 
// Thread t = new Thread(r);
// t.start(); ---> AppelezMyRunnableDansrun()
// Obtenir l'objet thread du pool thread ,Puis appelezMyRunnableDansrun()
service.submit(r);
// Obtenir plus d'objets thread ,AppelezMyRunnableDansrun()
service.submit(r);
service.submit(r);
// Attention!:submitUne fois l'appel de méthode terminé, La procédure ne prend pas fin , C'est parce que le pool de Threads contrôle la fermeture du thread .
// Retour du thread utilisé dans le pool de Threads 
// Fermer le pool de Threads
//service.shutdown();
}
}

CallableCode d'essai:

  • <T> Future<T> submit(Callable<T> task) : Obtenir un objet thread dans le pool thread,Et la mise en œuvre.

    Future : Représente le résultat du calcul .

  • V get() : Obtenir les résultats du calcul terminé .

public class ThreadPoolDemo2 {

public static void main(String[] args) throws Exception {

// Créer un objet threadpool
ExecutorService service = Executors.newFixedThreadPool(2);//Contient2Objets filetés
// CréationRunnableObjet instance
Callable<Double> c = new Callable<Double>() {

@Override
public Double call() throws Exception {

return Math.random();
}
};
// Obtenir l'objet thread du pool thread ,Puis appelezCallableDanscall()
Future<Double> f1 = service.submit(c);
// Futur Appelezget() Obtenir les résultats de l'opération
System.out.println(f1.get());
Future<Double> f2 = service.submit(c);
System.out.println(f2.get());
Future<Double> f3 = service.submit(c);
System.out.println(f3.get());
}
}

4.4 Exercice de pool de threads

Besoins: Exécuter les tâches en utilisant le pool de Threads ,Retour1-nEt

Analyse: Parce que le résultat de la somme doit être retourné ,Alors utilisezCallable Tâche de la méthode

Code:

public class Demo04 {

public static void main(String[] args) throws ExecutionException, InterruptedException {

ExecutorService pool = Executors.newFixedThreadPool(3);
SumCallable sc = new SumCallable(100);
Future<Integer> fu = pool.submit(sc);
Integer integer = fu.get();
System.out.println("Résultats: " + integer);
SumCallable sc2 = new SumCallable(200);
Future<Integer> fu2 = pool.submit(sc2);
Integer integer2 = fu2.get();
System.out.println("Résultats: " + integer2);
pool.shutdown();
}
}

SumCallable.java

public class SumCallable implements Callable<Integer> {

private int n;
public SumCallable(int n) {

this.n = n;
}
@Override
public Integer call() throws Exception {

// S'il te plaît.1-nEt?
int sum = 0;
for (int i = 1; i <= n; i++) {

sum += i;
}
return sum;
}
}

No5Chapitre Blocage

5.1 Qu'est - ce qu'une impasse

Dans un programme multithreadé, Plusieurs serrures sont utilisées , Provoque une attente mutuelle entre les fils . Le programme ne descend pas. .

5.2 Conditions de production de l'impasse

1. Combien de serrures y a - t - il?
2.Il y a plusieurs fils
3. Bloc de code de synchronisation imbriqué

5.3 Code d'impasse

public class Demo05 {

public static void main(String[] args) {

MyRunnable mr = new MyRunnable();
new Thread(mr).start();
new Thread(mr).start();
}
}
class MyRunnable implements Runnable {

Object objA = new Object();
Object objB = new Object();
/* Nidification1 objA Nidification1 objB Nidification2 objB Nidification1 objA */
@Override
public void run() {

synchronized (objA) {

System.out.println("Nidification1 objA");
synchronized (objB) {
// t2, objA, Je n'y arrive pas.BVerrouillage,Attendez.
System.out.println("Nidification1 objB");
}
}
synchronized (objB) {

System.out.println("Nidification2 objB");
synchronized (objA) {
// t1 , objB, Je n'y arrive pas.AVerrouillage,Attendez.
System.out.println("Nidification2 objA");
}
}
}
}

Attention!: On devrait essayer d'éviter les impasses.

No6Chapitre Visibilité

6.1 Regardez le programme et dites le résultat.

public class VolatileThread extends Thread {

// Définir les variables des membres
private boolean flag = false ;
public boolean isFlag() {

return flag;
}
@Override
public void run() {

try {

Thread.sleep(1000);
} catch (InterruptedException e) {

e.printStackTrace();
}
// Oui.flagLa valeur de est changée entrue
this.flag = true ;
System.out.println("flag=" + flag);
}
}
public class VolatileThreadDemo {
// Catégorie d'essai
public static void main(String[] args) {

// CréationVolatileThreadObjet thread
VolatileThread volatileThread = new VolatileThread() ;
volatileThread.start();
// mainMéthodes
while(true) {

if(volatileThread.isFlag()) {

System.out.println("Mise en œuvre======");
}
}
}
}

Résultats:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-GvKiUnAM-1641479930045)(imgs/Sans titre.jpg)]

Nous avons vu,VolatileThread Déjà dans le thread flagSet totrue,Mais...main() La méthode n'a jamais lu , Pour ne pas imprimer .

6.2 JMM

Généralités:JMM(Java Memory Model) JavaModèle de mémoire,- Oui.javaUn modèle de mémoire défini dans la spécification de la machine virtuelle.

JavaModèle de mémoire(Java Memory Model)DécritJavaDiverses variables dans le programme(Les Threads partagent des variables)Règles d'accès pour,Et dansJVMDétails sous - jacents tels que le stockage des variables en mémoire et la lecture des variables à partir de la mémoire.

Toutes les variables partagées sont stockées dans la mémoire principale.Les variables mentionnées ici se réfèrent àVariables d'instance et de classe.Ne contient pas de variables locales,Parce que la variable locale est privée au fil,Il n'y a donc pas de problème de concurrence.

**.Chaque fil a sa propre mémoire de travail,**Mémoire de travail du thread, Une copie de travail des variables utilisées par le thread est conservée . Toutes les actions du thread sur la variable (Lire,Prends - le.) Tout doit être fait en mémoire de travail ,Au lieu de lire et d'écrire directement les variables dans la mémoire principale,Il n'y a pas non plus d'accès direct aux variables de la mémoire de travail de l'autre côté entre les différents Threads, Le passage des valeurs des variables entre les Threads doit être effectué par la mémoire principale .

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-CPsLVGVz-1641479930045)(imgs/1561516472597.png)]

À propos deJMMQuelques conventions de synchronisation:

1.Avant que le thread ne se déverrouille,Les variables partagées doivent être effacées immédiatement dans la mémoire principale

2.Avant le verrouillage du fil,Vous devez lire la dernière valeur de la mémoire principale dans la mémoire de travail

3.Le verrouillage et le déverrouillage sont les mêmes

6.3 Analyse des problèmes

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-3Jjozpak-1641479930047)(imgs/1561303332516.png)]

  1. VolatileThread Le thread lit les données de la mémoire principale dans sa mémoire de travail correspondante
  2. Oui.flagLa valeur de est changée entrue,Mais à ce moment - là,flag La valeur de n'a pas été écrite à la mémoire principale
  3. En ce momentmain La méthode a été lue flagLa valeur defalse
  4. QuandVolatileThreadLe thread vaflag Après avoir écrit la valeur de ,MaismainDans la fonctionwhile(true) C'est le code sous - jacent du système qui est appelé ,Vite!, Presque trop tôt pour lire les valeurs dans la mémoire principale ,Alors...while(true) La valeur lue a toujours été false.( S'il y a un moment main Thread read from primary Memory to Primary Memory flagLa dernière valeur pour,Alorsif L'instruction peut être exécutée ,main .Quand le thread lit la dernière valeur de la mémoire principale ,On ne peut pas contrôler)

Lancer:

​ Plusieurs Threads accèdent aux variables partagées , Après qu'un thread modifie la valeur d'une variable , D'autres Threads ne voient pas la dernière valeur de la variable .

Résumé:

​ Programmation simultanée , Variable de modification multithreadée , L'invisibilité des variables inter - thread se produit .

Raison de l'invisibilité :

​ Chaque fil a sa propre mémoire de travail, Les Threads sont des copies des valeurs des variables partagées copiées à partir de la mémoire primaire .

​ Chaque thread manipule des variables partagées dans sa propre mémoire de travail .

6.4 Traitement des problèmes

Mode 1:Verrouillagesynchronized

UtilisersynchronizedMots clés:

 // mainMéthodes
while(true) {

synchronized (volatileThread) {

if(volatileThread.isFlag()) {

System.out.println("Mise en œuvre======");
}
}
}

Comment ça marche:

Un thread entre synchronized Avant et après le bloc de code , Le processus d'exécution est le suivant: :

a.Verrouillage par fil

b.Vider la mémoire de travail

c. De la dernière valeur de la variable de partage de copie de mémoire primaire à la mémoire de travail en tant que copie

d.Code d'exécution

e. Rafraîchir la valeur de la copie modifiée dans la mémoire principale

f.Verrouillage de la libération du fil

Mode 2:volatileMots clés

UtiliservolatileMots clés:

 private volatile boolean flag ;

Comment ça marche:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-oQZtJTre-1641479930049)(imgs/1561517963033.png)]

1.VolatileThread Le thread lit les données de la mémoire principale dans sa mémoire de travail correspondante

2.Oui.flagLa valeur de est changée entrue,Mais à ce moment - là,flag La valeur de n'a pas été écrite à la mémoire principale

3.En ce momentmainMéthodesmain La méthode a été lue flagLa valeur defalse

4.QuandVolatileThreadLe thread vaflag Après avoir écrit la valeur de , Échec d'un autre thread copie de cette variable

5.Encore une foisflag Le thread lit la valeur la plus récente de la mémoire principale pendant l'opération , Dans la mémoire de travail

Résumé: volatile Assurer la visibilité des différents Threads pour le fonctionnement des variables partagées ,Ce qui signifie qu'un thread a été modifiévolatileVariables modifiées, Lorsque les modifications sont écrites à la mémoire principale , Un autre thread a immédiatement vu la dernière valeur .

Phénomènes:

​ Programmation simultanée , Variable de modification multithreadée , L'invisibilité des variables inter - thread se produit .

Il existe deux façons courantes de résoudre l'invisibilité des variables entre les fils :

​ 1.Verrouillage

​ Résoudre les causes : Videra la mémoire de travail , Lire la dernière valeur de la mémoire principale dans la mémoire de travail .

​ 2. Pour les variables partagées volatileModification des mots clés

​ Résoudre les causes : Une fois qu'une variable dans un thread ,AjoutévolatileModificateur, D'autres fils peuvent lire les dernières valeurs immédiatement

6.5 volatileAvecsynchronizedLa différence entre

1).volatileSeules les variables d'instance et de classe peuvent être modifiées,EtsynchronizedPeut modifier la méthode,Et les blocs de code.

2).volatileGarantir la visibilité des données,Mais l'atomicité n'est pas garantie(C'est - à - dire::Multithreading for writing,La sécurité du fil n'est pas garantie);EtsynchronizedC'est une sorte d'exclusivité(Exclusion mutuelle)Mécanisme,Mise en œuvre de la sécurité des fils.

3).En termes de performance,volatileC'est mieux, Juste pour mettre en œuvre la visibilité des variables entre les fils .

No7Chapitre Atomicité

​ Généralités:Par atomicité, on entend une opération ou plusieurs opérations,Soit toutes les opérations sont effectuées et ne sont pas interrompues par quelque chose que ce soit,Soit toutes les opérations ne sont pas effectuées.

7.1 Regardez le programme et dites le résultat.

public class VolatileAtomicThread1 implements Runnable {

// Définir unintTraversée du type
private int count = 0 ;
@Override
public void run() {

// Effectuer sur cette variable ++Fonctionnement,100Une fois
for(int x = 1 ; x <= 100 ; x++) {

count++ ;
System.out.println("count =========>>>> " + count);
}
}
}
public class VolatileAtomicThreadDemo1 {

public static void main(String[] args) {

// CréationVolatileAtomicThreadObjet
VolatileAtomicThread1 volatileAtomicThread = new VolatileAtomicThread1() ;
// Ouvert100Paires de filscountEn cours++Fonctionnement
for(int x = 1 ; x <= 100 ; x++) {

new Thread(volatileAtomicThread).start();
}
}
}

Résultats de la mise en œuvre: Non, c'est sûr. 10000

7.2 Description du principe du problème

Les problèmes ci - dessus se posent principalement dans count++Fonctionnement:

count++L'opération contient3Étapes:

  • Lire les données de la mémoire principale à la mémoire de travail
  • Pour les données dans la mémoire de travail ++Fonctionnement
  • Écrivez les données de la mémoire de travail à la mémoire principale

count++ L'opération n'est pas atomique , C'est - à - dire l'exécution d'une action à un moment donné , Possibilité d'être interrompu par d'autres fils .

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-bwx2PvOT-1641479930050)(imgs/1561524132215.png)]

1)Supposons qu'en ce momentxLa valeur de100,ThreadA Besoin d'auto - augmentation de la quantité de changement 1Fonctionnement, Il faut d'abord lire les variables de la mémoire principale xValeur de.Parce queCPU Relations de commutation pour ,En ce momentCPU Le pouvoir exécutif a été transféré à BThread.ALe fil est prêt,BThread en cours d'exécution.

2)ThreadB Doit également être lu à partir de la mémoire principale xValeur de la variable,Parce que le threadANon, c'est vrai.x La valeur est modifiée de sorte que BLes données lues sont100.

3)ThreadB En mémoire de travail xMise en œuvre+1Fonctionnement, Mais pas rafraîchi dans la mémoire principale .

4)En ce momentCPU Le pouvoir exécutif est passé à ASur le fil,Parce que le threadB Les données de la mémoire de travail ne sont pas mises à jour à la mémoire principale ,Donc,A La valeur de la variable dans la mémoire de travail du thread est toujours 100,Pas d'échec..A Le thread a fait les données dans la mémoire de travail +1Fonctionnement.

5)ThreadBOui.101 Écrire à la mémoire principale .

6)ThreadAOui.101 Écrire à la mémoire principale .

Bien que calculé 2Une fois,Mais seulement pourAC'est fait.1Modifications.

7.3 volatile Essais d'atomicité

Test de code

// Définir unintVariable de type
private volatile int count = 0 ;

Résumé:Dans un environnement multithreadé,volatileLes mots clés garantissent la visibilité des données partagées,Mais cela ne garantit pas l'atomicité des opérations de données(Dans un environnement multithreadévolatile Les variables modifiées sont également dangereuses pour le thread ).

Dans un environnement multithreadé, Pour garantir la sécurité des données , Nous devons également utiliser le mécanisme de verrouillage .

volatileScénarios d'utilisation pour

  • Commande de commutation

    Tirer parti des caractéristiques de visibilité , Contrôler l'exécution ou l'arrêt d'un certain code ( Comme le premier cas du cours d'aujourd'hui ).

  • Variables partagées par plusieurs opérations de thread , Mais il y a un thread qui l'écrit , Les autres fils lisent

7.4 Résolution de problèmes

Mode 1:Utilisation du mécanisme de verrouillagesynchronized

​ Nous pouvons donnercount++ Opération ajouter une serrure ,Alorscount++ L'opération est le Code de la zone critique , La zone critique ne peut avoir qu'un seul thread à exécuter ,Alors...count++ C'est devenu une opération atomique .

public class VolatileAtomicThread2 implements Runnable {

// Définir unintVariable de type
private volatile int count = 0 ;
private static final Object obj = new Object();
@Override
public void run() {

// Effectuer sur cette variable ++Fonctionnement,100Une fois
for(int x = 1 ; x <= 100 ; x++) {

synchronized (obj) {

count++ ;
System.out.println("count =========>>>> " + count);
}
}
}
}
public class VolatileAtomicThreadDemo2 {

public static void main(String[] args) {

// CréationVolatileAtomicThreadObjet
VolatileAtomicThread2 volatileAtomicThread = new VolatileAtomicThread2() ;
// Ouvert100Paires de filscountEn cours++Fonctionnement
for(int x = 1 ; x <= 100 ; x++) {

new Thread(volatileAtomicThread).start();
}
}
}

Mode 2:Classe atomique

​ JavaDeJDK1.5A commencé à fournirjava.util.concurrent.atomicSac(AbréviationsAtomicSac),Les classes d'opérations atomiques de ce paquet offrent une utilisation simple,Performance efficace,Comment thread met à jour une variable en toute sécurité.

AtomicInteger

Type atomiqueInteger, Mise à jour atomique possible

public AtomicInteger(): Initialiser une valeur par défaut de 0 Type atomique Integer
public AtomicInteger(int initialValue): Initialiser le type atomique d'une valeur spécifiée Integer
int get(): Obtenir la valeur
int getAndIncrement(): Ajouter la valeur actuelle atomiquement à1,Attention!,Ce qui est retourné ici est la valeur avant l'augmentation.
int incrementAndGet(): Ajouter la valeur actuelle atomiquement à1,Attention!, Ce qui est retourné ici est la valeur depuis l'augmentation .
int addAndGet(int data): Comparer atomiquement la valeur saisie à la valeur dans l'Instance(AtomicIntegerÀ l'intérieur. value)Ajouter,Et renvoie les résultats.
int getAndSet(int value): Réglé atomiquement ànewValueValeur de,Et renvoie l'ancienne valeur.

Transformation des cas

UtiliserAtomicInteger Modification des cas .

public class VolatileAtomicThread3 implements Runnable {

// Définir unintVariable de type
private AtomicInteger atomicInteger = new AtomicInteger() ;
@Override
public void run() {

// Effectuer sur cette variable ++Fonctionnement,100Une fois
for(int x = 1; x <= 100 ; x++) {

int i = atomicInteger.getAndIncrement();
System.out.println("count =========>>>> " + i);
}
}
}
public class VolatileAtomicThreadDemo3 {

public static void main(String[] args) {

// CréationVolatileAtomicThreadObjet
VolatileAtomicThread3 volatileAtomicThread = new VolatileAtomicThread3() ;
// Ouvert100Paires de filscountEn cours++Fonctionnement
for(int x = 1 ; x <= 100 ; x++) {

new Thread(volatileAtomicThread).start();
}
}
}

7.5 Classe atomiqueCAS Mécanisme de sécurité des fils

7.5.1 Généralités

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-srx7PEH7-1641479930051)(imgs/CAS.png)]

CASSon nom complet est: Compare And Swap( Comparer et échanger );C'est moderne.CPUUne instruction spéciale largement prise en charge pour manipuler des données partagées en mémoire.

CASVous pouvezread-modify-check-write Conversion en opération atomique , Cette opération atomique est directement garantie par le processeur .

CASLe Mécanisme utilise3Opérandes de base:Adresse mémoireV,Anciennes attentesA,Nouvelle valeur à modifierB.

Exemples:

  1. Adresse mémoireVMilieu,La valeur stockée est10Variable de.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-F76Iq3d9-1641479930052)(imgs/1561550710005.png)]

  1. À ce stade, le thread1Pour augmenter la valeur de la variable1.Au fil1Dis,Anciennes attentesA=10,Nouvelle valeur à modifierB=11.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-TLwekSfm-1641479930053)(imgs/1561550793084.png)]

  1. Dans le thread1Avant de soumettre une mise à jour,Un autre fil2Un pas en avant.,Adresse mémoireVLa valeur de la variable dans a été mise à jour en premier à11.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-iyN2RqYJ-1641479930055)(imgs/1561550911597.png)]

  1. Thread1Commencer à soumettre des mises à jour,D'abord.AEt adresseVComparaison des valeurs réelles de(Compare),DécouverteAPas égal àVLa valeur réelle de,Échec de la soumission.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-YN8s0JZu-1641479930056)(imgs/1561551122602.png)]

  1. Thread1Récupérer l'adresse mémoireVValeur actuelle de,Et recalculer la nouvelle valeur que vous souhaitez modifier.Pour le moment, sur le fil1Dis,A=11,B=12.Ce processus de retry est appelé spin.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-9lxOFHDT-1641479930070)(imgs/1561551254968.png)]

  1. J'ai eu de la chance cette fois,Aucun autre thread n'a changé d'adresseVValeur de.Thread1En coursCompare,DécouverteAEt adresseVLa valeur réelle de est égale à.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-ZeUKeelq-1641479930071)(imgs/1561551329313.png)]

  1. Thread1En coursSWAP,AdresseVRemplacer la valeur parB,C'est - à - dire12.

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-eAqUGGJK-1641479930072)(imgs/1561551377905.png)]

Peut être utiliséAtomicIntegerDans la classepublic final boolean compareAndSet(int expect , int update)Méthode d'étalonnage.

7.5.2 CASRésumé

Comparez les valeurs de la mémoire de travail actuelle avec celles de la mémoire principale,Si cette valeur est attendue,Alors faites l'opération,Si ce n'est pas le cas, continuez à tourner.

Inconvénients:

1.Le cycle peut prendre du temps

2.L'atomicité d'une variable partagée ne peut être garantie qu'une seule fois

3.Ça va arriver.ABAQuestions( Il est recommandé d'introduire des références atomiques , Opération atomique avec numéro de version ,Par exemple:AtomicStampedReferenceDansgetStamp()Méthodes)

7.6 CASAvecSynchronized:Serrure optimiste et serrure pessimiste

Points communs:

CASEtSynchronized Peut garantir la sécurité des données partagées dans un environnement multithreadé .

C'est différent.:

Synchronized D'un point de vue pessimiste **(Serrure pessimiste)**

​ Toujours supposer le pire,Chaque fois que je vais chercher les données, je pense que quelqu'un d'autre va les modifier,Donc chaque fois que vous prenez des données, elles sont verrouillées,Pour que quelqu'un d'autre puisse bloquer ces données jusqu'à ce qu'il ait la serrure.(Les ressources partagées ne sont utilisées que par un seul thread à la fois,Autres fils bloqués,Transférer les ressources à d'autres Threads après utilisation).Donc,Synchronized Nous l'appelons aussi Serrure pessimiste.JDKDansReentrantLockC'est aussi une serrure pessimiste.Mauvais rendement!

CAS D'un point de vue optimiste **(La serrure optimiste)**

​ Toujours supposer le meilleur scénario,Chaque fois que je vais chercher les données, je pense que personne d'autre ne va les modifier,Pour ne pas verrouiller,Mais au fur et à mesure que vous mettez à jour les données, vous verrez si quelqu'un d'autre a mis à jour les données pendant cette période.CASCe mécanisme peut aussi être appelé une serrure optimiste.Bonne performance globale!

No8Chapitre Réarrangement des instructions

8.1 Qu'est - ce que le réarrangement?

​ Le réarrangement est le compilateur ou CPU Réorganisation structurelle du Code pour , Les meilleurs résultats d'exécution ont été obtenus . Le réarrangement peut être divisé en réarrangement du compilateur , Réarrangement du processeur .

// Réorganisation du compilateur
//Avant optimisation
int x = 1;
int y = 2;
int a1 = x * 1;
int b1 = y * 1;
int a2 = x * 2;
int b2 = y * 2;
//Après optimisation
int x = 1;
int y = 2;
int a1 = x * 1;
int a2 = x * 2;
int b1 = y * 1;
int b2 = y * 2;
//Grâce à cette optimisation,Peut - être pourCPU Il suffit de lire une fois xEtyValeur. Les registres originaux peuvent devoir être lus à plusieurs reprises pour alterner xEtyValeur de.
// Réarrangement du processeur 
//Initialisation:
int a = 0;
int b = 0;
int x = 0;
int y = 0;
//ProcesseurAMise en œuvre
a = 1; //A1
x = b; //A2 
//ProcesseurBMise en œuvre
b = 2; //B1
y = a; //B2 

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-vb4OQ8r3-1641479930073)(imgs/v2-a09f18abf6391e5c846ede38661c0261_720w.jpg)]

​ Parce que le processeur lit 、Écrire le cache, Le cache d'écriture n'a pas été mis à jour en mémoire à temps , Faire en sorte que les valeurs lues par d'autres processeurs ne soient pas à jour , Faire en sorte que les opérations de lecture et d'écriture effectuées par le processeur ne correspondent pas à l'ordre des réactions en mémoire .

​ L'exemple ci - dessus, Peut causer des processeurs AJe l'ai lu.b=0,ProcesseurBJe l'ai lu.a=0.A1Écris.a=1 Écrivez d'abord au processeur A Dans le cache d'écriture de , Dans la mémoire en ce moment a=0. Si le processeur BLire à partir de la mémoirea, Ce que vous lirez sera 0.

8.2 Cas de code

public class Barrier {

int a = 0;
int b = 0;
int x = 0;
int y = 0;
//Créer un pool de Threads pour un seul thread
private static ExecutorService executorService1= Executors.newSingleThreadExecutor();
private static ExecutorService executorService2= Executors.newSingleThreadExecutor();
private static ExecutorService executorService3= Executors.newSingleThreadExecutor();
public static void main(String ... args){

for (int i=0;i< 1000000;i++){

//Initialisation
Barrier barrier=new Barrier();
//ProcesseurAMise en œuvre
executorService1.submit(()->{

barrier.a = 1; //A1
barrier.x = barrier.b; //A2
print(barrier);
});
//ProcesseurBMise en œuvre
executorService2.submit(()->{

barrier.b = 2; //B1
barrier.y = barrier.a; //B2
print(barrier);
});
}
}
public static void print(Barrier barrier){

executorService3.submit(()->{

if(barrier.x==0 && barrier.y==0){

System.out.println("=======>"+barrier.a+" , "+barrier.b+" , "+barrier.x+" , "+barrier.y);
}else {

System.out.println(barrier.a+" , "+barrier.b+" , "+barrier.x+" , "+barrier.y);
}
});
}
}

Exécuter les résultats maintenant, Je vois., C'est possible. 0. Alors, avons - nous une solution à ce problème ? C'est là qu'on va parler de Barrière de mémoireC'est.

8.3 Barrière de mémoire

8.3.1 Généralités

Pour résoudre les problèmes susmentionnés, Le processeur est toujours utilisé pour fournir une arme ——Instruction de barrière de mémoire(Memory Barrier):

  1. Écrire La barrière de mémoire(Store Memory Barrier): Le processeur écrit la valeur du cache de stockage actuel dans le cache primaire , D'une manière bloquée .
  2. Lire la barrière de mémoire(Load Memory Barrier): Le processeur traite les files d'attente invalides , D'une manière bloquée .

​ En ajoutant une barrière de mémoire , La visibilité des données entre les deux opérations est garantie . volatileLe mot - clé passe par“Barrière de mémoire” Pour empêcher que les instructions ne soient réorganisées , volatile Une barrière de lecture est insérée avant de lire les données , Après avoir écrit les données, ajoutez une barrière d'écriture ,Alors...,Ça peut éviterCPU Problèmes causés par le réarrangement , Réaliser la visibilité des données entre plusieurs fils .

8.3.2 Principaux types de barrières mémoire

Pour réaliservolatileSémantique de la mémoire pour,Quand le compilateur génère le Bytecode,Une barrière de mémoire est insérée dans la séquence d'instruction pour empêcher un type particulier de réorganisation du processeur.Et pourtant,Pour le compilateur,Il est presque impossible de trouver une disposition optimale pour minimiser le nombre total de barrières insérées,À cette fin,,Java Le modèle de mémoire adopte une politique prudente .

Ce qui suit est basé sur une stratégie conservatriceJMMPolitique d'insertion de barrière mémoire:
Dans chaquevolatileInsérer un avant l'opération d'écritureStoreStoreBarrière.
Dans chaquevolatileInsérer un après l'opération d'écritureStoreLoadBarrière.
Dans chaquevolatileInsérer un après l'opération de lectureLoadLoadBarrière.
Dans chaquevolatileInsérer un avant l'opération de lectureLoadStoreBarrière.

Principaux types de barrières mémoire (Ce qui suit suffit:)
Différents matériels implémentent la barrière mémoire différemment,JavaLe modèle de mémoire masque les différences de cette plate - forme matérielle sous - jacente,ParJVMPour générer le code machine correspondant pour différentes plates - formes.

JavaLes barrières de mémoire sont principalementLoadEtStoreDeux catégories.
C'est exact.Load BarrierDis,Insérer une barrière de lecture avant de lire la commande,Peut invalider les données dans le cache,Recharger les données de la mémoire principale
C'est exact.Store BarrierDis,Insérer une barrière d'écriture après l'instruction d'écriture,Permet d'écrire les dernières données écrites dans le cache à la mémoire principale

PourLoadEtStore,Dans la pratique, Il est divisé en quatre catégories: :

LoadLoad Barrière
Séquence:Load1,Loadload,Load2
Assurez - vous queLoad1 Les données à lire peuvent être lues par Load2Et ensuiteload Lire avant l'accès à la commande . Il est généralement possible d'exécuter des instructions de précharge ou / Et nécessite une déclaration explicite dans les processeurs qui supportent le brouillage LoadloadBarrière, Parce que les instructions de chargement en attente dans ces processeurs peuvent contourner les instructions en attente de stockage . Pour les processeurs qui garantissent toujours l'ordre de traitement , La mise en place de cette barrière équivaut à l'absence de fonctionnement .

StoreStore Barrière
Séquence:Store1,StoreStore,Store2
Assurez - vous queStore1Les données deStore2Et suiviStore Visible à d'autres processeurs avant l'opération d'instruction ( Par exemple, rafraîchir les données dans la mémoire principale ).En général, Si le processeur ne peut pas garantir le tampon d'écriture ou / Et cache pour rafraîchir les données séquentiellement dans d'autres processeurs et le cache principal , Alors il faut l'utiliser StoreStoreBarrière.

LoadStore Barrière
Séquence: Load1; LoadStore; Store2
Assurez - vous queLoad1Les données deStore2 Et suivi Store Lire avant de rafraîchir l'instruction .En attenteStore Les instructions peuvent être dépassées loads L'instruction doit être utilisée sur le processeur de brouillage LoadStoreBarrière.

StoreLoad Barrière
Séquence: Store1; StoreLoad; Load2
Assurez - vous queStore1 Les données de Load2Et ensuiteLoad Visible à d'autres processeurs avant la lecture de l'instruction .StoreLoad La barrière peut empêcher un loadDirectives Utilisation incorrecte Store1Données, Au lieu d'un autre processeur qui écrit une nouvelle donnée au même endroit mémoire .C'est pourquoi, Donc le processeur dont il est question ci - dessous, pour lire les données stockées dans le même emplacement de mémoire devant la barrière ,Vous devez utiliser unStoreLoad La barrière sépare les instructions de stockage des instructions de chargement suivantes .Storeload La barrière est nécessaire dans presque tous les multiprocesseurs modernes , Mais d'habitude, c'est aussi le plus cher . Ils sont chers en partie parce qu'ils doivent fermer le mécanisme habituel de lecture des données directement à partir du tampon d'écriture en sautant le cache . Cela peut se faire en laissant un tampon se rafraîchir complètement (flush), Et d'autres façons de retarder la réalisation .

8.3.3 Exemple

Voici une stratégie conservatrice,volatileLire le schéma de séquence d'instructions généré après l'insertion de la barrière mémoire:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-SfvOhLHB-1641479930073)(imgs/Barrière de mémoire1.png)]

Ci - dessusvolatileÉcrire etvolatileLa stratégie d'insertion de la barrière de mémoire en lecture est très conservatrice.Dans la pratique,Tant que ça ne change pas.volatileÉcris.-Sémantique de la mémoire lue,Le compilateur peut omettre les barrières inutiles au cas par cas.

Voici un exemple de code spécifique pour illustrer:

class VolatileBarrierExample {

int a;
volatile int v1 = 1;
volatile int v2 = 2;
void readAndWrite() {

int i = v1; // Le premiervolatileLire
int j = v2; // DeuxièmevolatileLire
a = i + j; //Écrivez
v1 = i + 1; // Le premiervolatileÉcris.
v2 = j * 2; // Deuxième volatileÉcris.
}//Autres méthodes
}

PourreadAndWrite()Méthodes,Le compilateur peut effectuer les optimisations suivantes lors de la génération du Bytecode:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-xtd52SJw-1641479930074)(imgs/Barrière de mémoire2.png)]

8.4 volatileRésumé

volatile- Oui.JavaMécanisme de synchronisation léger fourni par la machine virtuelle

1.Garantie de visibilité

2.L'atomicité n'est pas garantie

3.Désactiver le réarrangement des commandes

volatileEst capable de maintenir la visibilité,L'atomicité n'est pas garantie,En raison de la barrière de mémoire,Il est possible d'éviter le réarrangement des commandes.

No9Chapitre Et le contrat

InJDK Plusieurs conteneurs et classes d'outils simultanés très utiles sont disponibles dans le paquet simultané de . Pour notre utilisation dans le développement multithreadé .

9.1 ConcurrentHashMap

Pourquoi utiliserConcurrentHashMap:

  1. HashMapThread Unsafe,Cela peut entraîner une confusion des données
  2. Utilisez thread SafeHashtableInefficacité

Pour les deux raisons ci - dessus,Et voilà.ConcurrentHashMapL'occasion d'entrer.

  • HashMapThread Unsafe Demo.

Public、Collection statique:

public class Const {

public static HashMap<String,String> map = new HashMap<>();
}

Thread,VersmapÉcrire des données dans:

public void run() {

for (int i = 0; i < 500000; i++) {

Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
}
System.out.println(this.getName() + " Fin!");
}

Catégorie d'essai:

public class Demo {

public static void main(String[] args) throws InterruptedException {

Thread1A a1 = new Thread1A();
Thread1A a2 = new Thread1A();
a1.setName("Thread1-");
a2.setName("Thread2-");
a1.start();
a2.start();
//Repos10Secondes,Assurez - vous que les deux Threads sont terminés
Thread.sleep(1000 * 5);
//Imprimer la taille de la collection
System.out.println("MapTaille:" + Const.map.size());
}
}

Description:Les deux fils sont dirigés vers le mêmemapIn writing50000Paires de valeurs clés,EnfinmapDesizeDevrait être:100000,Mais plusieurs fois de plus, vous trouverez les erreurs suivantes:

  1. Faux mort.:

    [Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-bcen5yfO-1641479930074)(imgs/aba6.png)]

  2. Anomalie:

    [Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-8nCKwYgC-1641479930075)(imgs/HashMapAnomalie.png)]

  3. Mauvais résultat:

    [Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-DQjMy05n-1641479930075)(imgs/HashMapMauvais résultat.png)]

  • Pour assurer la sécurité des fils,Peut être utiliséHashtable.Attention!: Le timing est ajouté au fil

    Public、Collection statique:

    public class Const {
    
    public static Hashtable<String,String> map = new Hashtable<>();
    }
    

    Thread,VersmapÉcrire des données dans:

    public void run() {
    
    long start = System.currentTimeMillis();
    for (int i = 0; i < 500000; i++) {
    
    Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
    }
    long end = System.currentTimeMillis();
    System.out.println(this.getName() + " Fin!Temps d'utilisation:" + (end - start) + " MS");
    }
    

    Catégorie d'essai:

    public class Demo {
    
    public static void main(String[] args) throws InterruptedException {
    
    Thread1A a1 = new Thread1A();
    Thread1A a2 = new Thread1A();
    a1.setName("Thread1-");
    a2.setName("Thread2-");
    a1.start();
    a2.start();
    //Repos10Secondes,Assurez - vous que les deux Threads sont terminés
    Thread.sleep(1000 * 5);
    //Imprimer la taille de la collection
    System.out.println("MapTaille:" + Const.map.size());
    }
    }
    

    Résultats de la mise en œuvre:

    [Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-QjpgSbL6-1641479930076)(imgs/HashtableTemps d'exécution.png)]

Je vois.,Hashtable Sécurité du fil garantie ,Le temps est2Plus de secondes..

  • Regarde encoreConcurrentHashMap

    Public、Collection statique:

    public class Const {
    
    public static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
    }
    

    Thread,VersmapÉcrire des données dans:

    public void run() {
    
    long start = System.currentTimeMillis();
    for (int i = 0; i < 500000; i++) {
    
    Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
    }
    long end = System.currentTimeMillis();
    System.out.println(this.getName() + " Fin!Temps d'utilisation:" + (end - start) + " MS");
    }
    

    Catégorie d'essai:

    public class Demo {
    
    public static void main(String[] args) throws InterruptedException {
    
    Thread1A a1 = new Thread1A();
    Thread1A a2 = new Thread1A();
    a1.setName("Thread1-");
    a2.setName("Thread2-");
    a1.start();
    a2.start();
    //Repos10Secondes,Assurez - vous que les deux Threads sont terminés
    Thread.sleep(1000 * 5);
    //Imprimer la taille de la collection
    System.out.println("MapTaille:" + Const.map.size());
    }
    }
    

    Résultats de la mise en œuvre:

    [Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-AmitskT3-1641479930077)(imgs/ConcurrentHashMapTemps d'exécution.png)]

    ConcurrentHashMap On peut toujours s'assurer que les résultats sont corrects , Et plus efficace .

HashTableCauses des inefficacités:

public synchronized V put(K key, V value)
public synchronized V get(Object key)

HashTableUtilisation du conteneursynchronizedPour sécuriser les fils,Mais avec des fils très compétitifsHashTableTrès inefficace.Parce que quand un thread accèdeHashTableMéthode de synchronisation pour,D'autres fils accèdent égalementHashTableLors de la synchronisation de,Va entrer dans un état de blocage.Comme un fil1UtiliserputAjouter des éléments,Thread2Non seulement il ne peut pas être utiliséputMéthode ajouter un élément,Et ne peut pas être utiliségetMéthode pour obtenir l'élément,Donc plus la concurrence est forte, plus l'efficacité est faible..

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-xKlQR4f9-1641479930078)(imgs/Hashtable Schéma de verrouillage .png)]

ConcurrentHashMapDes raisons d'efficacité:CAS + Local(synchronized)Verrouillé.Serrure segmentée

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-up4Cw8j8-1641479930078)(imgs/ConcurrentHashMap Schéma de verrouillage .png)]

9.2 CountDownLatch

CountDownLatchPermettre à un ou plusieurs Threads d'attendre que d'autres Threads terminent l'opération,Et s'exécuter à nouveau.

Par exemple:Thread1Pour imprimer:AEtC,Thread2Pour imprimer:B,Mais les fils1ImpressionAAprès,Pour thread2ImprimerBImprimer aprèsC,Alors...:Thread1ImpressionAAprès,Doit attendre le thread2Fin de l'impressionBAvant de procéder.

CountDownLatchMéthode de construction:

public CountDownLatch(int count)// Initialisation d'un compteur spécifiéCountDownLatchObjet

CountDownLatchMéthodes importantes:

public void await() throws InterruptedException// Laisser le thread actuel attendre
public void countDown() // Compteur à soustraire1
  • Exemple
    1). Créer des fils1:
public class ThreadA extends Thread {

private CountDownLatch down ;
public ThreadA(CountDownLatch down) {

this.down = down;
}
@Override
public void run() {

System.out.println("A");
try {

down.await();
} catch (InterruptedException e) {

e.printStackTrace();
}
System.out.println("C");
}
}

2). Créer des fils2:

public class ThreadB extends Thread {

private CountDownLatch down ;
public ThreadB(CountDownLatch down) {

this.down = down;
}
@Override
public void run() {

System.out.println("B");
down.countDown();
}
}

3). Créer une classe d'essai :

public class Demo {

public static void main(String[] args) {

CountDownLatch down = new CountDownLatch(1);//Création1Compteurs
new ThreadA(down).start();
new ThreadB(down).start();
}
}

4). Résultats de la mise en œuvre:
Je promets d'appuyer sur :A B CImpression séquentielle de.

Description:

CountDownLatchMoyennecount down C'est le compte à rebours. ,latch C'est le sens du loquet. . Le sens général peut être compris comme le compte à rebours , Un peu. “Trois, deux, un.,Sésame ouvre la porte”Le sentiment de.

CountDownLatchEst réalisé par un compteur,Chaque fois qu'un thread termine sa tâche,Peut être appelécountDown()Comment faire un compteur-1,Quand le compteur arrive0Heure,AppelezCountDownLatchDeawait()L'état de blocage du fil de la méthode est libéré,Poursuivre la mise en œuvre.

9.3 CyclicBarrier

Généralités

CyclicBarrierEst littéralement recyclable(Cyclic)Barrière(Barrier).Ce qu'il doit faire,Laissez un groupe de fils atteindre une barrière(Ou un point de synchronisation)Est bloqué,Jusqu'à ce que le dernier fil atteigne la barrière,La barrière s'ouvrira,Tous les Threads bloqués par la barrière continueront à fonctionner.

Par exemple:Convocation de la société5Réunion des employés,Attendez.5Tous les employés sont là.,Ouverture de la session.

Nous créons5Threads of Employees,1Fils de réunion,Démarrage presque simultané,UtiliserCyclicBarrierGarantie5Une fois que tous les Threads des employés ont été exécutés,Exécuter à nouveau le fil de réunion.

CyclicBarrierMéthode de construction:

public CyclicBarrier(int parties, Runnable barrierAction)// Utilisé lorsque le fil atteint la barrière,Mise en œuvre prioritairebarrierAction,Faciliter le traitement de scénarios d'affaires plus complexes

CyclicBarrierMéthodes importantes:

public int await()// Chaque thread appelleawaitMéthodeCyclicBarrierJ'ai atteint la barrière.,Puis le fil courant est bloqué
  • Exemple de code:
    1). Créer des fils d'employés :
public class PersonThread extends Thread {

private CyclicBarrier cbRef;
public PersonThread(CyclicBarrier cbRef) {

this.cbRef = cbRef;
}
@Override
public void run() {

try {

Thread.sleep((int) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " On y est.! ");
cbRef.await();
} catch (InterruptedException e) {

e.printStackTrace();
} catch (BrokenBarrierException e) {

e.printStackTrace();
}
}
}

2). Créer un fil de réunion :

public class MeetingThread extends Thread {

@Override
public void run() {

System.out.println("C'est bon,Tout le monde est là.,La séance est ouverte.......");
}
}

3). Créer une classe d'essai :

public class Demo {

public static void main(String[] args) {

CyclicBarrier cbRef = new CyclicBarrier(5, new MeetingThread());//Attendez.5Exécution des Threads terminée,Re - exécutionMeetingThread
PersonThread p1 = new PersonThread(cbRef);
PersonThread p2 = new PersonThread(cbRef);
PersonThread p3 = new PersonThread(cbRef);
PersonThread p4 = new PersonThread(cbRef);
PersonThread p5 = new PersonThread(cbRef);
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}

4). Résultats de la mise en œuvre:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-lzt96A9s-1641479930079)(imgs/CyclicBarrier_1.png)]

Utiliser le scénario

Utiliser le scénario:CyclicBarrierPeut être utilisé pour calculer les données multithreadées,Dernier scénario de fusion des résultats calculés.

Besoins:Lire avec deux threads2Données dans les fichiers,Lorsque les données des deux fichiers ont été lues,Résumer les données.

9.4 Semaphore

Semaphore(Signalez.)La fonction principale de la commande est de contrôler le nombre de Threads simultanés.

synchronizedPeut se lever"Verrouillage"Le rôle de,Mais pendant un certain temps,,Un seul thread est autorisé à exécuter.

SemaphorePeut être configuré pour permettre plusieurs Threads d'exécuter simultanément.

SemaphoreLittéralement sémaphore,Son rôle est de contrôler le nombre de Threads accédant à une ressource spécifique.

SemaphoreMéthode de construction:

public Semaphore(int permits) permits Représente le nombre de Threads de licence
public Semaphore(int permits, boolean fair) fair Représentation de l'équité,Si c'est réglé à true Et si,Le prochain thread à exécuter sera le thread le plus attendu

SemaphoreMéthodes importantes:

public void acquire() throws InterruptedException Signifie obtenir une licence
public void release() release() Indique l'autorisation de libération
  • Exemple 1:En même temps1Exécution du thread

1). Faire unServiceCatégorie:

public class Service {

private Semaphore semaphore = new Semaphore(1);//1Signifie licence,Indique le maximum autorisé1Exécution du threadacquire()Etrelease()Contenu entre
public void testMethod() {

try {

semaphore.acquire();
System.out.println(Thread.currentThread().getName()
+ " Entrée Temps=" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()
+ " Fin Temps=" + System.currentTimeMillis());
semaphore.release();
//acquire()Etrelease()Le Code entre les méthodes est"Code de synchronisation"
} catch (InterruptedException e) {

e.printStackTrace();
}
}
}

2). Créer une classe de thread :

public class ThreadA extends Thread {

private Service service;
public ThreadA(Service service) {

super();
this.service = service;
}
@Override
public void run() {

service.testMethod();
}
}

3). Catégorie d'essai:

public class Demo {

public static void main(String[] args) {

Service service = new Service();
//Démarrage5Threads
for (int i = 1; i <= 5; i++) {

ThreadA a = new ThreadA(service);
a.setName("Thread " + i);
a.start();//5Les Threads s'exécutent simultanémentServiceDetestMethodMéthodes,Et il ne peut y avoir qu'une seule période de temps1Exécution du thread
}
}
}

4). Résultats:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-bkXmGqNX-1641479930081)(imgs/Semaphore1Exécution du thread.png)]

  • Exemple 2:En même temps2Threads perform at the same time
    1). ModifierServiceCatégorie,Oui.new Semaphore(1)Lire comme suit:2C'est tout.:
public class Service {

private Semaphore semaphore = new Semaphore(2);//2Signifie licence,Indique le maximum autorisé2Exécution du threadacquire()Etrelease()Contenu entre
public void testMethod() {

try {

semaphore.acquire();
System.out.println(Thread.currentThread().getName()
+ " Entrée Temps=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()
+ " Fin Temps=" + System.currentTimeMillis());
semaphore.release();
//acquire()Etrelease()Le Code entre les méthodes est"Code de synchronisation"
} catch (InterruptedException e) {

e.printStackTrace();
}
}
}

2). Exécuter à nouveau les résultats :

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-ZJkSJrlw-1641479930082)(imgs/Semaphore2Exécution du thread.png)]

9.5 Exchanger

Généralités

Exchanger(Échangeur)Est une classe d'outils pour la collaboration entre threads.ExchangerPour l'échange de données entre threads.

Ces deux fils passentexchangeMéthode d'échange de données,Si le premier thread exécute en premierexchange()Méthodes,Il attendra que le deuxième thread s'exécute.exchangeMéthodes,Quand les deux fils atteignent le point de synchronisation,Ces deux fils peuvent échanger des données,Transmettre les données générées par ce thread à l'autre partie.

ExchangerMéthode de construction:

public Exchanger()

ExchangerMéthodes importantes:

public V exchange(V x)
  • Exemple 1:exchangeCaractéristiques de blocage de la méthode

1).Créer des filsA,Et capable de recevoirExchangerObjet:

public class ThreadA extends Thread {

private Exchanger<String> exchanger;
public ThreadA(Exchanger<String> exchanger) {

super();
this.exchanger = exchanger;
}
@Override
public void run() {

try {

System.out.println("ThreadAValeur à transmettre'Les cadeauxA'Au filB,Et attendre le threadBValeur de...");
System.out.println("Dans le threadAThread inBValeur de=" + exchanger.exchange("Les cadeauxA"));
} catch (InterruptedException e) {

e.printStackTrace();
}
}
}

2). Productionmain()Méthodes:

public class Demo {

public static void main(String[] args) {

Exchanger<String> exchanger = new Exchanger<String>();
ThreadA a = new ThreadA(exchanger);
a.start();
}
}

3).Résultats de la mise en œuvre:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-0oarmgKl-1641479930082)(imgs/ExchangeBlocage.png)]

  • Exemple 2:exchangeMéthode d'exécution de l'échange

1).Créer des filsA:

public class ThreadA extends Thread {

private Exchanger<String> exchanger;
public ThreadA(Exchanger<String> exchanger) {

super();
this.exchanger = exchanger;
}
@Override
public void run() {

try {

System.out.println("ThreadAValeur à transmettre'Les cadeauxA'Au filB,Et attendre le threadBValeur de...");
System.out.println("Dans le threadAThread inBValeur de=" + exchanger.exchange("Les cadeauxA"));
} catch (InterruptedException e) {

e.printStackTrace();
}
}
}

2).Créer des filsB:

public class ThreadB extends Thread {

private Exchanger<String> exchanger;
public ThreadB(Exchanger<String> exchanger) {

super();
this.exchanger = exchanger;
}
@Override
public void run() {

try {

System.out.println("ThreadBValeur à transmettre'Les cadeauxB'Au filA,Et attendre le threadAValeur de...");
System.out.println("Dans le threadBThread inAValeur de=" + exchanger.exchange("Les cadeauxB"));
} catch (InterruptedException e) {

e.printStackTrace();
}
}
}

3). Créer une classe d'essai :

public class Demo {

public static void main(String[] args) throws InterruptedException {

Exchanger<String> exchanger = new Exchanger<String>();
ThreadA a = new ThreadA(exchanger);
ThreadB b = new ThreadB(exchanger);
a.start();
b.start();
}
}

4).Résultats de la mise en œuvre:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-pj8Wp7zb-1641479930083)(imgs/ExchangeEchange.png)]

  • Exemple 3:exchangeDélai de la méthode

1).Créer des filsA:

public class ThreadA extends Thread {

private Exchanger<String> exchanger;
public ThreadA(Exchanger<String> exchanger) {

super();
this.exchanger = exchanger;
}
@Override
public void run() {

try {

System.out.println("ThreadAValeur à transmettre'Les cadeauxA'Au filB,Et attendre le threadBValeur de,Attends.5Secondes...");
System.out.println("Dans le threadAThread inBValeur de =" + exchanger.exchange("Les cadeauxA",5, TimeUnit.SECONDS));
System.out.println("ThreadAFin!");
} catch (InterruptedException e) {

e.printStackTrace();
} catch (TimeoutException e) {

System.out.println("5Les secondes n'ont pas attendu le filBValeur de,ThreadAFin!");
}
}
}

2). Créer une classe d'essai :

public class Run {

public static void main(String[] args) {

Exchanger<String> exchanger = new Exchanger<String>();
ThreadA a = new ThreadA(exchanger);
a.start();
}
}

3).Résultats des tests:

[Impossible de transférer l'image de la chaîne externe,Il peut y avoir un mécanisme antivol à la station source,Il est recommandé de sauvegarder l'image et de la télécharger directement(img-JUpO6MAV-1641479930083)(imgs/ExchangeTemps mort.png)]

Utiliser le scénario

Utiliser le scénario: Peut faire la correction des données

Besoins:Par exemple, nous devons entrer manuellement le flux bancaire en papier dans le flux bancaire électronique..Pour éviter les erreurs,AdoptionABDeux personnes pour entrer, Entrée dans deux fichiers , Le système doit charger ces deux fichiers , Et vérifier les données de deux fichiers ,Voir si les entrées sont cohérentes.

版权声明:本文为[Vers le Haut]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/01/202201080601055870.html