Compréhension du problème des 8 verrous entre les verrous d'objets synchronisés multithreads et les verrous statiques

8 problème de serrure

Il y a deux problèmes : le type de verrou et qui
le verrouille. Le type de verrou : les verrous statiques concernent les objets de toutes les instances de cette classe et les verrous d'objet
ne concernent que l'objet courant. À qui est destiné le verrousynchronized关键字 ? 非静态La méthode du verrou statique sont toutes les méthodes avec synchronizeddes mots-clés et de ce type. Pour les deux points ci-dessus, lors du jugement, faites attention à : s'il s'agit du même objet, et quel type de type de verrou est la méthode appelée par l'objet静态

Verrou 1 : plusieurs threads utilisent le même objet pour appeler différentes méthodes non statiques avec le mot-clé synchronized

Analyse : Le verrou d'objet est uniquement pour l'objet actuel et le verrou est pour toutes les méthodes non statiques avec le mot clé synchronized.

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
   }
class Phone{
    
    
    synchronized void sendSMG(){
    
    
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

Résultat :
insérez la description de l'image ici
1re pensée : 为什么A线程与B线程之间启动要睡一会?
le code est exécuté de manière séquentielle, mais le système d'exploitation peut décider du démarrage du thread. Bien que A passe en premier à l'état prêt, le système d'exploitation peut choisir que B démarre en premier. Laissez le thread A et le thread B dormir pendant un certain temps lorsqu'ils démarrent, pour vous assurer que le thread A doit démarrer en premier. Parce que le traitement du système d'exploitation est extrêmement rapide, même si l'intervalle est de 1 s, il y aura une grande différence de temps pour le système d'exploitation. Comme A et B ne sont pas ensemble, A a une grande probabilité de démarrer en premier.

Pensée 2 : 为什么结果是这样?
Deux points clés :
c'est le même objet et le type de verrou est le même
. Un thread démarre en premier et obtient le verrou d'objet. Le verrou d'objet sera libéré) et la méthode avec le verrou d'objet synchronisé ne peut pas être appelée. , et ne peut qu'attendre. Jusqu'à ce que le thread A termine son exécution et libère le verrou, le thread B peut acquérir le verrou et continuer à s'exécuter.

Pensée 3 : 既然sendSMG()方法先执行,如果在方法中睡一会,sendCall()方法会不会先执行?
Comme mentionné précédemment, le thread A exécute d'abord la méthode sendSMG() après avoir obtenu le verrou, car sleep() dans la méthode ne libérera pas le verrou, même si le temps d'exécution est trop long, le verrou ne sera pas libéré avant l'exécution est terminée, B Vous ne pouvez pas obtenir le verrou pour exécuter la méthode avec un verrou d'objet synchronisé.
Réflexion 4 : 如果Phone有一个普通方法,没有带synchronized锁,B线程启动后即使sendSMG()方法没有执行完毕,这个普通方法会被执行吗?
Il n'y a pas de verrou d'objet dans la méthode ordinaire et B n'a pas besoin d'attendre que A libère le verrou lors de l'exécution , il peut donc être exécuté immédiatement tant que B démarre. Si le thread A appelle également cette méthode commune, alors les deux threads A et B peuvent être dans cette méthode en même temps.

Verrou 2 : Sur la base du verrou 1, augmente le temps d'exécution de la méthode exécutée par le thread A, afin que B ait la possibilité de participer à l'exécution

Pour vérifier la pensée 3 ci-dessus, cela prouve que la méthode sendSMG() est exécutée en premier, non que le thread A démarre l'exécution en premier et rattrape le problème que le thread B appelle la méthode sendCall(), mais A ne le fait pas exécutez la méthode sendSMG() pour libérer le verrou d'objet, et B Il n'y aura simplement aucune chance d'exécuter la méthode sendCall(). Comme le thread B démarre après un certain laps de temps, le verrou d'objet est d'abord obtenu par A. Tant que le verrou est obtenu, au sein d'un objet, plusieurs threads ne peuvent avoir qu'un seul thread pour exécuter la méthode avec le verrou d'objet synchronisé.

public class Application {
    
    

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

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2); // 注意:我这里特意为2s使得这是B线程一定启动了 测试:B线程的sendCasll方法会不会先执行。} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

Le code augmente simplement le temps d'exécution de sendMsg() sur la base du verrou 1. Le résultat est le même que pour le verrou 1. Après avoir attendu 1 seconde, la console affichera "envoyer un message", puis "appeler" apparaîtra
insérez la description de l'image ici
.
insérez la description de l'image ici
Une fois que le thread A a saisi le verrou d'objet, il exécute la méthode sendSMG(), affiche "send message" sur la console, puis dort pendant 1 s et libère le verrou. Le thread B obtient le verrou, exécute la méthode sendCall() et affiche immédiatement "call" sur la console

Verrou 3 : plusieurs threads utilisent le même objet, un thread exécute une méthode avec un verrou d'objet et un thread exécute une méthode normale

Analyse : le verrou d'objet verrouille la méthode non statique avec synchronized, et la méthode ordinaire ne verrouille plus la portée.
Devinez : la méthode ordinaire n'est pas affectée par la synchronisation, le thread qui s'exécute le plus rapidement peut être appelé en premier.

public class Application {
    
    

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

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendHello();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
    void sendHello(){
    
    
        System.out.println("hello");
    }
}

Résultat :
insérez la description de l'image ici
l'exécution du thread B n'a pas besoin d'attendre la libération du verrou d'objet du thread A, car la méthode ordinaire n'a pas du tout besoin d'acquérir le verrou d'objet.

Réflexion : Que se passera-t-il si un thread appelle également la méthode normale, puis réajuste la méthode normale comme ceci ?
insérez la description de l'image ici
insérez la description de l'image ici
résultat:
insérez la description de l'image ici


Ce qui précède utilise le même objet pour discuter du problème des verrous d'objets synchronisés, et ce qui suit utilise 不同des objets pour discuter du problème des verrous d'objets synchronisés


Verrou 4 : plusieurs threads utilisent différents objets pour appeler différentes méthodes avec des verrous d'objet

Analyse : Verrouillage d'objet : uniquement valide pour l'objet actuel, il n'est donc pas possible de l'utiliser entre différents objets

public class Application {
    
    

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

        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
    
    phone1.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone2.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
    void sendHello(){
    
    
        System.out.println(Thread.currentThread().getName()+"进入");
        try {
    
     TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {
    
     }
        System.out.println("hello");
        System.out.println(Thread.currentThread().getName()+"离开");
    }
}

Résultat :
insérez la description de l'image ici
Réflexion : 明明A线程先启动获取了对象锁,为什么B线程的方法在A线程没有执行完毕就开始执行?
Il y a deux problèmes : le type de verrou et qui le verrouille.
Les threads A et B utilisent des objets différents, il n'est donc pas valide pour les verrous d'objet ! L'objet opéré par le thread B a son propre verrou de thread B, et l'objet opéré par le thread A a son propre verrou de thread A. Les deux ne sont pas le même objet, donc les verrous sont différents. Pour les méthodes qui exécutent des types de verrous d'objet, le verrou d'objet du thread A ne peut pas verrouiller le verrou d'objet du thread
B 如果锁住不同对象的?
used , générant un verrou également reconnu par différents objets.


Le verrou statique sera présenté ci-dessous


Verrou 5 : plusieurs threads appellent des méthodes sur leurs verrous statiques respectifs sur le même objet

Analyse : Verrous statiques, tous les objets de ce type sont valides. Locked est une méthode statique avec synchronisation

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
     synchronized static void sendSMG(){
    
    

        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
     synchronized static void sendCall(){
    
    
        System.out.println("打电话");
    }
}

Effet :
insérez la description de l'image ici
l'ajout de statique à la méthode équivaut au même verrou de classe, qu'il s'agisse du même objet ou non, tant qu'il s'agit d'un objet instancié de ce type, aucun objet ne peut s'exécuter tant que l'objet n'acquiert pas le verrou et ne le libère pas. méthode it avec verrou de classe

Verrou 6 : plusieurs threads et plusieurs objets appellent des méthodes sur leurs verrous statiques respectifs

L'effet est le même que celui du verrou 5. Dans les méthodes qui sont toutes des verrous de classe, il ne distinguera pas s'il s'agit du même objet. Tant qu'un objet est en cours d'exécution, qu'il s'agisse du même objet ou non, il ne peut qu'attendre d'exécuter la méthode avec un verrou de classe dans cette classe.
Pensée : 如果线程A使用对象1调用的是静态锁方法,线程B使用对象2调用的是普通方法,A先启动,那么对象2调用普通方法是需要等待A释放静态锁吗?
Bien sûr que non, car les méthodes ordinaires n'ont aucun verrou et les verrous de classe ne peuvent pas le limiter.


Ce qui suit mélange les verrous statiques avec les verrous d'objet


Verrouillage 7 : plusieurs threads appellent respectivement la méthode de verrouillage d'objet et la méthode de verrouillage statique sur le même objet

Analyse : Il existe des verrous d'objet et des verrous statiques, et il n'y a aucune relation entre eux.
Ou faites attention à deux points clés : Est-ce le même objet ? Oui ; la méthode est-elle appelée le même type de verrou ? 不是, l'un est un verrou statique, l'autre est un verrou d'objet ;
deux méthodes différentes, car les types de verrous sont différents, il n'y a pas de relation entre eux

public class Application {
    
    

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

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    

        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    static synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

Effet :
insérez la description de l'image ici
le thread A s'exécute en premier, mais le thread A obtient un verrou d'objet. Après le démarrage du thread B, il obtient un verrou de classe, qui ne peut pas être verrouillé par un verrou d'objet, de sorte que B n'a pas à attendre que A finisse de s'exécuter et libère le verrou de classe avant d'aller implémenter.

Pensée 1 : 如果是两个线程调用的同一个方法加上静态锁呢?
cela n'a rien à voir avec le fait qu'il s'agisse du même objet, car le verrouillage d'une méthode statique est un verrou au niveau de la classe, qu'il s'agisse du même objet ou non, tant qu'un thread acquiert le verrou, avant le verrou est libéré après l'exécution, aucun autre thread ne peut exécuter cette méthode. Mais d'autres méthodes de verrouillage non statiques peuvent être mises en œuvre.

Pensée 2 : 如果A先拿到类锁,B后拿到对象锁,那B用等到A释放锁后再去执行吗?也就是说类锁与对象锁是否有包含关系?(différents objets)
insérez la description de l'image ici

Preuve : les verrous de classe et les verrous d'objet sont deux types de verrous avec des portées différentes (les verrous d'objet ne concernent que l'objet actuel et les verrous de classe concernent les objets de toutes les instances de cette classe), mais les verrous de classe n'incluent pas les verrous d'objet.

Verrouillage 8 : différents objets de plusieurs threads appellent respectivement la méthode de verrouillage d'objet et la méthode de verrouillage statique

Identique au verrou 7 pensant 2, ce que le thread A obtient est le verrou d'objet appartenant à cet objet 1, et ce que le thread B obtient est le verrou de classe appartenant à tous les objets. Les types de verrous utilisés par les deux méthodes d'appel sont différents.Celui qui s'exécute le plus vite sortira en premier, et il n'y a pas de problème d'attente.

Je suppose que tu aimes

Origine blog.csdn.net/m0_52889702/article/details/128847416
conseillé
Classement