Maison  >  Article  >  Java  >  J'ai entendu dire que c'était une question d'entretien Java difficile ?

J'ai entendu dire que c'était une question d'entretien Java difficile ?

王林
王林avant
2021-01-11 09:54:052376parcourir

J'ai entendu dire que c'était une question d'entretien Java difficile ?

Jetons d'abord un coup d'œil au contenu de la question :

(Partage de vidéos d'apprentissage : tutoriel vidéo Java)

public class TestSync2 implements Runnable {
    int b = 100;          

    synchronized void m1() throws InterruptedException {
        b = 1000;
        Thread.sleep(500); //6
        System.out.println("b=" + b);
    }

    synchronized void m2() throws InterruptedException {
        Thread.sleep(250); //5
        b = 2000;
    }

    public static void main(String[] args) throws InterruptedException {
        TestSync2 tt = new TestSync2();
        Thread t = new Thread(tt);  //1
        t.start(); //2

        tt.m2(); //3
        System.out.println("main thread b=" + tt.b); //4
    }

    @Override
    public void run() {
        try {
            m1();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Le résultat de ce programme ?

Résultats de sortie du programme

main thread b=2000
b=1000
或
main thread b=1000
b=1000

Inspecter les points de connaissance

synchroniser le verrouillage de l'instance.

Visibilité de la mémoire en simultanéité.

(Recommandé pour des questions d'entretien plus connexes : questions et réponses d'entretien Java)

En Java, les programmes multithread sont les plus difficiles à comprendre et à déboguer, et de nombreux fois, les résultats de l'exécution n'ont pas fonctionné aussi bien que nous le pensions. Par conséquent, le multithreading en Java est particulièrement difficile.Je me souviens vaguement que lorsque je passais le test de langage C niveau 2 à l'université, les questions étaient ++ et de nombreuses autres questions prioritaires portant sur le résultat final que je souhaitais. pour répondre à certaines de ces questions concernant la priorité des opérateurs et l'associativité. Le mémoriser suffit, mais le multithreading Java doit encore être bien compris, et le mémoriser ne fonctionnera pas.

Commençons par une analyse simple :

Cette question implique 2 threads (thread principal, sous-thread), et les mots-clés impliquent synchronisé et Thread.sleep.
Le mot-clé synchronisé est assez compliqué (peut-être que parfois il n'est pas bien compris, donc la question ci-dessus sera un peu mal comprise). Sa fonction est de réaliser la synchronisation des threads (il existe de nombreuses façons d'obtenir la synchronisation des threads, c'est juste un). méthode qui sera abordée dans les articles suivants). Oui, vous devez étudier certaines implémentations du maître Doug Lea). Son travail consiste à verrouiller le code qui doit être synchronisé, afin qu'un seul thread puisse entrer dans le bloc de synchronisation à la fois. time (en fait une stratégie pessimiste) pour garantir que le thread ne se souvient que de la sécurité.

Utilisation du mot-clé général synchronisé

Spécifiez l'objet de verrouillage : verrouillez l'objet donné, et le verrouillage de l'objet donné doit être activé avant de saisir le code de synchronisation. Agir directement sur la méthode de l'instance : équivalent à verrouiller l'instance en cours. Avant de saisir le code de synchronisation, vous devez obtenir le verrou de l'instance en cours. Agir directement sur les méthodes statiques : équivalent à verrouiller la classe courante. Avant de saisir le code de synchronisation, il faut obtenir le verrou de la classe courante.

Dans le code ci-dessus, l'utilisation synchronisée appartient en fait au deuxième cas. Agir directement sur la méthode de l'instance : équivalent à verrouiller l'instance en cours. Avant de saisir le code de synchronisation, vous devez obtenir le verrou de l'instance en cours.

Malentendus possibles

En raison d'une compréhension insuffisante de synchronisé, nos multi-threads fonctionnent souvent avec une méthode synchronisée lorsque deux threads appellent deux méthodes synchronisées différentes, c'est un malentendu de penser qu'il y en a. il n'y a aucune relation lors de l'utilisation de méthodes. Agir directement sur la méthode de l'instance : équivalent à verrouiller l'instance en cours. Avant de saisir le code de synchronisation, vous devez obtenir le verrou de l'instance en cours. Si l'on appelle la méthode synchronisée. Peu importe si l'autre appelle une méthode normale, et il n'y a pas de relation d'attente entre les deux.

Ceux-ci sont très utiles pour une analyse ultérieure.

Thread.sleep

amène le thread actuel (c'est-à-dire le thread qui appelle cette méthode) à suspendre l'exécution pendant un certain temps pour donner aux autres threads une chance de continuer l'exécution, mais cela ne libère pas le verrouillage de l'objet. Autrement dit, s’il existe un bloc de synchronisation synchronisé, les autres threads ne peuvent toujours pas accéder aux données partagées. Notez que cette méthode doit détecter les exceptions, ce qui est très utile pour une analyse ultérieure. Pour plus de détails, veuillez vous référer à ma System Learning Java High Concurrency Series 2.

Processus d'analyse :

Java est exécuté à partir de la méthode principale. Il est mentionné ci-dessus qu'il y a 2 threads, mais il est inutile de modifier ici la priorité des threads. La commande n'est valable que lorsqu'ils n'ont pas été exécutés. Désormais, dès que ce code est exécuté, le thread principal main a été exécuté. Pour la variable d'attribut int b =100, il n'y aura aucun problème de visibilité dû à l'utilisation de synchronisé (il n'est pas nécessaire d'utiliser une déclaration volatile), lors de l'exécution de l'étape 1 (Thread t = new Thread(tt); //1) fil Il est dans le nouvel état et n'a pas encore commencé à fonctionner.

Lors de l'exécution des 2 étapes (t.start(); //2) lorsque la méthode start est appelée, le thread est effectivement démarré et entre dans l'état exécutable. L'état exécutable indique qu'il peut être exécuté et. tout est prêt, mais cela ne signifie pas qu'il doit être exécuté sur le CPU. Son exécution dépend de la planification du CPU de service. Lors de l'exécution de l'étape 3 ici, vous devez d'abord obtenir le verrou (car start doit appeler la méthode native, et tout est prêt une fois l'utilisation terminée, mais cela ne signifie pas qu'il doit être exécuté sur le CPU. Que ce soit réellement exécutée dépend de la planification du CPU de service (la méthode run sera appelée ultérieurement et la méthode m1 sera exécutée).

En fait, peu importe que Thread.sheep dans les deux méthodes synchronisées soit utilisé ici, c'est probablement juste pour augmenter la difficulté de confusion. Lorsque l'étape 3 est exécutée, le thread enfant est en fait bientôt prêt, mais en raison de l'existence de synchronisé et du fait qu'il travaille sur le même objet, le thread enfant doit attendre. Étant donné que l'ordre d'exécution dans la méthode principale est séquentiel, l'étape 4 doit être terminée après l'étape 3. Puisque l'étape 3 est terminée, le sous-thread peut exécuter m1.

Il y a un problème ici parmi les multithreads qui l'obtiennent en premier. S'il l'obtient en premier à l'étape 4, alors le thread principal b=2000. Si le sous-thread m1 l'obtient, b peut avoir été attribué à 1000 ou à lui. sera affiché à l'étape 4 avant d'avoir le temps de l'attribuer. Le résultat possible est le thread principal b=1000 ou le thread principal b=2000. Si l'étape 6 est supprimée ici, alors b= est exécuté en premier et le thread principal b= est le premier. , ce qui est incertain. Mais comme les 6 étapes existent, quoi qu'il en soit, le fil principal b= est à l'avant, donc cela dépend de la situation s'il est 1000 ou 2000. Après cela, b=1000 est définitivement fixé.

Quelques suggestions pour le multi-threading

Les threads sont également très précieux, il est donc recommandé d'utiliser des pools de threads, je les partagerai plus tard. et vous devez en être conscient. Donnez un nom au thread Lorsque le processeur en ligne est élevé, vous devez utiliser jstack avancé. Ce sera beaucoup plus pratique s'il y a un nom. Le multithreading nécessite une attention particulière aux problèmes de sécurité des threads, et vous devez également comprendre si jdk est thread-safe ou non, afin qu'aucun problème inexplicable ne se produise lors de son utilisation.

Il y a aussi certaines compétences qui seront partagées dans les articles suivants. Le multithreading est particulièrement important et difficile. J'espère que tout le monde y consacrera plus de temps.

Quelques compétences de débogage pour le multi-threading

En raison des points d'arrêt, tous les threads doivent s'arrêter lorsqu'ils passent le point d'arrêt, ce qui entraîne l'interruption constante de ce point, ce qui est très inconfortable. Il existe des points d'arrêt conditionnels. vous permettent de vous arrêter lorsque les conditions sont remplies, c'est donc pratique.

Recommandations associées : Tutoriel d'introduction à Java

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer