JavaRush /Blog Java /Random-FR /Pause café #113. 5 choses que vous ne saviez probablement...

Pause café #113. 5 choses que vous ne saviez probablement pas sur le multithreading en Java. 10 extensions JetBrains pour lutter contre la dette technique

Publié dans le groupe Random-FR

5 choses que vous ne saviez probablement pas sur le multithreading en Java

Source : DZone Thread est le cœur du langage de programmation Java. Même l'exécution du programme Hello World nécessite le thread principal. Si nécessaire, nous pouvons ajouter d'autres threads au programme si nous voulons que notre code d'application soit plus fonctionnel et performant. Si nous parlons d'un serveur Web, il traite simultanément des centaines de requêtes. Plusieurs threads sont utilisés pour cela. Pause café #113.  5 choses que vous ne saviez probablement pas sur le multithreading en Java.  10 extensions JetBrains pour lutter contre la dette technique - 1Les threads sont sans aucun doute utiles, mais travailler avec eux peut être difficile pour de nombreux développeurs. Dans cet article, je partagerai cinq concepts multithreading que les développeurs nouveaux et expérimentés ne connaissent peut-être pas.

1. L'ordre du programme et l'ordre d'exécution ne correspondent pas

Lorsque nous écrivons du code, nous supposons qu’il s’exécutera exactement de la manière dont nous l’écrivons. Cependant, en réalité, ce n’est pas le cas. Le compilateur Java peut modifier l'ordre d'exécution pour l'optimiser s'il peut déterminer que la sortie ne changera pas dans le code monothread. Regardez l'extrait de code suivant :
package ca.bazlur.playground;

import java.util.concurrent.Phaser;

public class ExecutionOrderDemo {
    private static class A {
        int x = 0;
    }

    private static final A sharedData1 = new A();
    private static final A sharedData2 = new A();

    public static void main(String[] args) {
        var phaser = new Phaser(3);
        var t1 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l1 = sharedData1;
            var l2 = l1.x;
            var l3 = sharedData2;
            var l4 = l3.x;
            var l5 = l1.x;
            System.out.println("Thread 1: " + l2 + "," + l4 + "," + l5);
        });
        var t2 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l6 = sharedData1;
            l6.x = 3;
            System.out.println("Thread 2: " + l6.x);
        });
        t1.start();
        t2.start();
        phaser.arriveAndDeregister();
    }
}
Ce code semble simple. Nous avons deux instances de données partagées ( sharedData1 et sharedData2 ) qui utilisent deux threads. Lorsque nous exécutons le code, nous nous attendons à ce que le résultat ressemble à ceci :
Fil 2 : 3 Fil 1 : 0,0,0
Mais si vous exécutez le code plusieurs fois, vous verrez un résultat différent :
Fil 2 : 3 Fil 1 : 3,0,3 Fil 2 : 3 Fil 1 : 0,0,3 Fil 2 : 3 Fil 1 : 3,3,3 Fil 2 : 3 Fil 1 : 0,3,0 Fil 2 : 3 Fil 1 : 0,3,3
Je ne dis pas que tous ces flux seront lus exactement comme ça sur votre machine, mais c'est tout à fait possible.

2. Le nombre de threads Java est limité

Créer un thread en Java est simple. Cependant, cela ne signifie pas que nous pouvons en créer autant que nous le souhaitons. Le nombre de fils est limité. Nous pouvons facilement savoir combien de threads nous pouvons créer sur une machine particulière en utilisant le programme suivant :
package ca.bazlur.playground;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Playground {
    public static void main(String[] args) {
        var counter = new AtomicInteger();
        while (true) {
            new Thread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            }).start();
        }
    }
}
Le programme ci-dessus est très simple. Il crée un thread dans une boucle puis le gare, ce qui signifie que le thread est désactivé pour une utilisation future mais effectue un appel système et alloue de la mémoire. Le programme continue de créer des threads jusqu'à ce qu'il ne puisse plus en créer, puis lève une exception. Nous sommes intéressés par le numéro que nous recevrons jusqu'à ce que le programme lève une exception. Sur mon ordinateur, je n'ai pu créer que 4065 threads.

3. Trop de threads ne garantissent pas de meilleures performances

Il est naïf de croire que la simplicité des threads en Java améliorera les performances des applications. Malheureusement, cette hypothèse est fausse avec notre modèle multithread traditionnel que Java propose aujourd'hui. En fait, trop de threads peuvent réduire les performances d’une application. Posons d'abord cette question : quel est le nombre maximum optimal de threads que nous pouvons créer pour maximiser les performances de l'application ? Eh bien, la réponse n'est pas si simple. Cela dépend beaucoup du type de travail que nous effectuons. Si nous avons plusieurs tâches indépendantes, qui sont toutes informatiques et ne bloquent aucune ressource externe, alors avoir un grand nombre de threads n'améliorera pas beaucoup les performances. En revanche, si nous disposons d'un processeur à 8 cœurs, le nombre optimal de threads pourrait être (8 + 1). Dans un tel cas, nous pouvons nous appuyer sur le thread parallèle introduit dans Java 8. Par défaut, le thread parallèle utilise le pool Fork/Join partagé. Il crée des threads égal au nombre de processeurs disponibles, ce qui est suffisant pour qu'ils fonctionnent de manière intensive. Ajouter plus de threads à un travail gourmand en CPU où rien n'est bloqué n'améliorera pas les performances. Au contraire, nous gaspillerons simplement des ressources. Note. La raison pour laquelle il y a un thread supplémentaire est que même un thread à forte intensité de calcul provoque parfois une erreur de page ou est suspendu pour une autre raison. (Voir : Java Parallelism in Practice , Brian Goetz, page 170) Cependant, supposons, par exemple, que les tâches soient liées aux E/S. Dans ce cas, ils dépendent de communications externes (par exemple base de données, autres API), donc un plus grand nombre de threads est logique. La raison en est que lorsqu'un thread attend sur l'API Rest, les autres threads peuvent continuer à fonctionner. Maintenant, nous pouvons nous demander à nouveau : combien de threads sont de trop pour un tel cas ? Dépend. Il n’existe pas de chiffres parfaits qui conviennent à tous les cas. Par conséquent, nous devons effectuer des tests adéquats pour découvrir ce qui fonctionne le mieux pour notre charge de travail et notre application spécifiques. Dans le scénario le plus typique, nous avons généralement un ensemble de tâches mixtes. Et dans de tels cas, les choses vont à leur terme. Dans son livre « Java Concurrency in Practice », Brian Goetz a proposé une formule que nous pouvons utiliser dans la plupart des cas. Nombre de threads = Nombre de cœurs disponibles * (1 + Temps d'attente / Temps de service) Le temps d'attente peut être égal à IO, comme l'attente d'une réponse HTTP, l'acquisition d'un verrou, etc. Temps de service(Temps de service) est le temps de calcul, tel que le traitement d'une réponse HTTP, le marshaling/unmarshaling, etc. Par exemple, une application appelle une API puis la traite. Si nous avons 8 processeurs sur le serveur d'applications, que le temps de réponse moyen de l'API est de 100 ms et que le temps de traitement des réponses est de 20 ms, alors la taille de thread idéale serait :
N = 8 * (1 + 100/20) = 48
Cependant, il s’agit là d’une simplification excessive ; des tests adéquats sont toujours essentiels pour déterminer le nombre.

4. Le multithreading n'est pas du parallélisme

Parfois, nous utilisons de manière interchangeable le multithreading et le parallélisme, mais cela n’est plus tout à fait pertinent. Bien qu'en Java nous obtenions les deux en utilisant un thread, ce sont deux choses différentes. « En programmation, le multithreading est un cas particulier quels que soient les processus en cours d'exécution, et le parallélisme est l'exécution simultanée de calculs (éventuellement liés). Le multithreading consiste à interagir avec beaucoup de choses en même temps. La concurrence fait beaucoup de choses en même temps. La définition ci-dessus donnée par Rob Pike est assez précise. Disons que nous avons des tâches complètement indépendantes et qu'elles peuvent être calculées séparément. Dans ce cas, ces tâches sont dites parallèles et peuvent être exécutées avec un pool Fork/Join ou un thread parallèle. En revanche, si nous avons de nombreuses tâches, certaines d’entre elles peuvent dépendre d’autres. La façon dont nous composons et structurons s’appelle le multithreading. Cela a à voir avec la structure. Nous pouvons vouloir effectuer plusieurs tâches simultanément pour obtenir un certain résultat, sans nécessairement en terminer une plus rapidement.

5. Project Loom nous permet de créer des millions de fils de discussion

Dans le point précédent, j'ai soutenu qu'avoir plus de threads ne signifie pas une amélioration des performances des applications. Cependant, à l’ère des microservices, nous interagissons avec trop de services pour effectuer un travail spécifique. Dans un tel scénario, les threads restent la plupart du temps bloqués. Même si un système d'exploitation moderne peut gérer des millions de sockets ouverts, nous ne pouvons pas ouvrir de nombreux canaux de communication car nous sommes limités par le nombre de threads. Mais que se passe-t-il si vous créez des millions de threads et que chacun d’eux utilise un socket ouvert pour communiquer avec le monde extérieur ? Cela améliorera certainement le débit de nos applications. Pour soutenir cette idée, il existe une initiative en Java appelée Project Loom. En l'utilisant, nous pouvons créer des millions de fils virtuels. Par exemple, en utilisant l'extrait de code suivant, j'ai pu créer 4,5 millions de threads sur ma machine.
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Main {
    public static void main(String[] args) {
        var counter = new AtomicInteger();

        // 4_576_279
        while (true) {
            Thread.startVirtualThread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            });
        }
    }
}
Pour exécuter ce programme, vous devez avoir installé Java 18, qui peut être téléchargé ici . Vous pouvez exécuter le code à l'aide de la commande suivante : java --source 18 --enable-preview Main.java

10 extensions JetBrains pour lutter contre la dette technique

Source : DZone De nombreuses équipes de développement ressentent une énorme pression pour respecter les délais. Pour cette raison, ils n’ont souvent pas suffisamment de temps pour réparer et nettoyer leur base de code. Parfois, dans ces situations, la dette technique s’accumule rapidement. Les extensions de l'éditeur peuvent aider à résoudre ce problème. Jetons un coup d'œil aux 10 meilleures extensions JetBrains pour lutter contre la dette technique (avec support Java). Pause café #113.  5 choses que vous ne saviez probablement pas sur le multithreading en Java.  10 extensions JetBrains pour lutter contre la dette technique - 2

Outils de refactoring et de dette technique

1. RefactorInsight

RefactorInsight améliore la visibilité des modifications de code dans l'EDI en fournissant des informations sur les refactorisations.
  1. L'extension définit les refactorisations dans les demandes de fusion.
  2. Marque les commits qui contiennent des refactorisations.
  3. Aide à visualiser la refactorisation de tout commit spécifique sélectionné dans l'onglet Git Log.
  4. Affiche l'historique de refactorisation des classes, des méthodes et des champs.

2. Suivi des problèmes Stepsize dans l'IDE

Stepsize est un excellent outil de suivi des problèmes pour les développeurs. L'extension aide les ingénieurs non seulement à créer de meilleurs TODO et commentaires de code, mais également à prioriser la dette technique, les refactorisations, etc. :
  1. Stepsize vous permet de créer et d'afficher des tâches dans le code directement dans l'éditeur.
  2. Recherchez les problèmes qui affectent les fonctionnalités sur lesquelles vous travaillez.
  3. Ajoutez des tickets à vos sprints à l'aide des intégrations Jira, Asana, Linear, Azure DevOps et GitHub.

3. Nouveau flux de code relique

New Relic CodeStream est une plateforme de collaboration entre développeurs pour discuter et réviser le code. Il prend en charge les requêtes pull de GitHub, BitBucket et GitLab, la gestion des problèmes de Jira, Trello, Asana et 9 autres, et propose des discussions sur le code, reliant le tout.
  1. Créez, examinez et fusionnez des demandes d'extraction dans GitHub.
  2. Obtenez des commentaires sur les travaux en cours grâce aux révisions préliminaires du code.
  3. Discuter des problèmes de code avec ses coéquipiers.

À FAIRE et commentaires

4. Surligneur de commentaires

Ce plugin vous permet de créer une mise en évidence personnalisée des lignes de commentaires et des mots-clés de langue. Le plugin a également la possibilité de définir des jetons personnalisés pour mettre en évidence les lignes de commentaires.

5. De meilleurs commentaires

L'extension Better Comments vous aide à créer des commentaires plus clairs dans votre code. Avec cette extension vous pourrez classer vos annotations en :
  1. Alertes.
  2. Demandes.
  3. FAIRE.
  4. Moments fondamentaux.

Bugs et failles de sécurité

6.SonarLint _

SonarLint vous permet de résoudre les problèmes de code avant qu'ils ne surviennent. Il peut également être utilisé comme correcteur orthographique. SonarLint met en évidence les bogues et les vulnérabilités de sécurité au fur et à mesure que vous codez, avec des instructions de remédiation claires afin que vous puissiez les corriger avant que le code ne soit validé.

7. Repérer les bogues

Le plugin SpotBugs fournit une analyse statique du bytecode pour rechercher des bogues dans le code Java d'IntelliJ IDEA. SpotBugs est un outil de détection de défauts pour Java qui utilise l'analyse statique pour trouver plus de 400 modèles de bogues tels que les déréférences de pointeurs nuls, les boucles récursives infinies, l'utilisation abusive des bibliothèques Java et les blocages. SpotBugs peut identifier des centaines de défauts graves dans des applications volumineuses (généralement environ 1 défaut pour 1 000 à 2 000 lignes de déclarations brutes non commentées).

8. Scanner de vulnérabilités Snyk

Le scanner de vulnérabilités de Snyk vous aide à trouver et à corriger les vulnérabilités de sécurité et les problèmes de qualité de code dans vos projets.
  1. Trouver et résoudre les problèmes de sécurité.
  2. Affichez une liste de différents types de problèmes, divisés en catégories.
  3. Affiche des conseils de dépannage.

9. Localisateur de caractères de largeur nulle

Ce plugin améliore la vérification et la détection des erreurs difficiles à trouver liées aux caractères invisibles de largeur nulle dans le code source et les ressources. Lors de l'utilisation, assurez-vous que la coche « Caractère Unicode de largeur nulle » est activée.

10.CodeMR _

CodeMR est un outil d'analyse de qualité logicielle et de code statique qui aide les éditeurs de logiciels à développer de meilleurs codes et programmes. CodeMR visualise les métriques de code et les attributs de qualité de haut niveau (couplage, complexité, cohésion et taille) dans diverses vues telles que les vues Structure du package, TreeMap, Sunburst, Dépendance et Graphique.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION