Maison  >  Article  >  Java  >  Les principes et scénarios d'utilisation de Synchronized en Java et l'analyse de l'utilisation et des différences de l'interface Callable

Les principes et scénarios d'utilisation de Synchronized en Java et l'analyse de l'utilisation et des différences de l'interface Callable

WBOY
WBOYavant
2023-04-21 08:04:071113parcourir

    1. Fonctionnalités de base

    1. Cela commence par un verrouillage optimiste si les conflits de verrouillage sont fréquents, il sera converti en verrouillage pessimiste

    2. Cela commence par une mise en œuvre légère du verrouillage. longtemps, longtemps, il est converti en un verrou lourd.

    3. La stratégie de verrouillage rotatif qui est la plus probablement utilisée lors de la mise en œuvre de verrous légers

    4 C'est un verrou injuste

    5.

    6. Pas un verrou en lecture-écriture

    2. Processus de verrouillage

    JVM divise les verrous synchronisés en états de verrouillage sans verrouillage, biaisés, légers et lourds. Il sera mis à niveau séquentiellement en fonction de la situation.

    Les principes et scénarios dutilisation de Synchronized en Java et lanalyse de lutilisation et des différences de linterface Callable

    Bias Lock

    Supposons que le protagoniste masculin est un verrou et que le protagoniste féminin est un fil s'il n'y a qu'un seul fil pour utiliser ce verrou, alors même si le protagoniste masculin et le protagoniste féminin n'obtiennent pas de fil. certificat de mariage (en évitant les opérations coûteuses), vous pouvez vivre heureux pour toujours. Mais la protagoniste féminine apparaît et essaie de rivaliser pour le protagoniste masculin. À ce moment-là, peu importe le coût de l'opération d'obtention d'un certificat de mariage, la protagoniste féminine. doit terminer cette action, faisant abandonner la protagoniste féminine

    Le biais n'est pas de verrouiller. En réalité, le "verrouillage" consiste simplement à faire une "marque de biais de verrouillage" dans l'en-tête de l'objet pour enregistrer à quel thread appartient le verrou s'il y en a. aucun autre thread ne rivalisera pour le verrou à l'avenir, alors il n'est pas nécessaire d'effectuer d'autres opérations de synchronisation (ce qui évite d'avoir à ajouter des verrous). S'il y a d'autres threads en compétition pour le verrou plus tard (ce qui). le thread auquel appartient le verrou actuel a été enregistré dans l'objet de verrouillage, il est facile d'identifier si le thread demandant actuellement le verrou est le thread précédemment enregistré), puis annulez l'original. L'état de verrouillage biaisé entre dans l'état de verrouillage léger général

    Le verrouillage biaisé est essentiellement équivalent au "verrouillage différé". Si vous ne pouvez pas le verrouiller, ne le verrouillez pas et essayez d'éviter une surcharge de verrouillage inutile. Mais ce qu'il faut faire Le marquage doit encore être fait, sinon ce sera le cas. impossible de distinguer quand un vrai verrouillage est requis

    Le verrouillage biaisé n'est pas vraiment un verrouillage, mais enregistre simplement une marque dans l'en-tête de l'objet du verrou (enregistrant le thread auquel le verrou appartient). Si aucun autre thread ne participe aux verrous concurrents, alors le. L'opération de verrouillage ne sera pas réellement effectuée, réduisant ainsi la surcharge du programme. Une fois que d'autres threads seront réellement impliqués dans la compétition, l'état de verrouillage biaisé sera annulé et l'état de verrouillage léger sera entré

    Verrouillage léger

    Avec les autres threads, entrez en compétition. , l'état de verrouillage biaisé est éliminé et l'état de verrouillage léger (verrouillage de rotation adaptatif) est entré. Le verrouillage léger ici est implémenté via CAS

    Vérifiez et mettez à jour un morceau de mémoire via CAS (comme null => Référencé par. ce fil)

    Si la mise à jour réussit, le verrou est considéré comme réussi

    Si la mise à jour échoue, le verrou est considéré comme occupé et l'attente de type rotation continue (sans abandonner le processeur).

    L'opération de rotation est Keeping La mise au ralenti du processeur est un gaspillage de ressources CPU. Par conséquent, la rotation ici ne continuera pas éternellement, mais cessera de tourner après avoir atteint un certain temps/nombre de tentatives. C'est ce qu'on appelle "adaptatif"

    Verrouillage lourd

    Si. la compétition devient plus intense et le spin ne peut pas obtenir le statut de verrouillage rapidement, il se transformera en un verrou lourd. Le verrou lourd fait ici référence à l'utilisation du mutex fourni par le noyau.

    Pour effectuer l'opération de verrouillage, entrez d'abord le noyau. state.

    Déterminez si le verrou actuel est occupé dans l'état du noyau

    Si le verrou n'est pas occupé, le verrouillage est réussi et le commutateur revient à l'état utilisateur.

    Si le verrou est occupé, le verrouillage échoue. À ce moment-là, le fil entre dans la file d'attente du verrou et se bloque. En attendant d'être réveillé par le système d'exploitation.

    Après une série de changements, le verrou a été libéré par d'autres fils. Le système d'exploitation a également mémorisé le fil suspendu. il a réveillé le thread et a essayé de le redémarrer. Acquérir le verrou

    3 Autres opérations d'optimisation

    Élimination du verrouillage

    Le compilateur + JVM détermine si le verrou peut être éliminé. Si c'est possible, éliminez-le simplement directement

    Certains. les codes d'application sont synchronisés, mais en fait, ils ne sont pas utilisés dans de nombreuses applications (comme StringBuffer)

    StringBuffer sb = new StringBuffer();
    sb.append("a");
    sb.append("b");
    sb.append("c");
    sb.append("d");

    À l'heure actuelle, chaque appel d'ajout impliquera le verrouillage et le déverrouillage. un seul thread, alors ces opérations de verrouillage et de déverrouillage sont inutiles et gaspillées. Une certaine surcharge de ressources.

    Leader, attribuez des tâches de travail aux subordonnés

    Méthode 1 :

    Appelez, donnez la tâche 1, raccrochez.

    Les principes et scénarios dutilisation de Synchronized en Java et lanalyse de lutilisation et des différences de linterface CallableAppelez, donnez la tâche 2, raccrochez.

    Appelez, donnez la tâche 3, raccrochez

    Méthode 2 :

    Appelez, donnez la tâche 1, la tâche 2, la tâche 3, raccrochez le téléphone

    4. Interface appelable

    Qu'est-ce que Callable ?

    Callable est une interface qui équivaut à encapsuler une "valeur de retour" dans un. thread. Il est pratique pour les programmeurs de calculer les résultats en utilisant le multi-threading.

    Callable 和 Runnable 相对, 都是描述一个 "任务". Callable 描述的是带有返回值的任务, Runnable 描述的是不带返回值的任务.Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为 Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定. FutureTask 就可以负责这个等待结果出来的工作.

    代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本

    public class Text {
     
        static class Result{
            public int sum = 0;
            public Object locker = new Object();
        }
     
        public static void main(String[] args) throws InterruptedException {
            Result result = new Result();
     
            Thread t = new Thread(){
                @Override
                public void run() {
                    int sum = 0;
                    for (int i = 0; i <=10000; i++){
                        sum += i;
                    }
                    result.sum = sum;
     
                    synchronized (result.locker){
                        result.locker.notify();
                    }
                }
            };
            t.start();
            synchronized (result.locker){
                while (result.sum == 0){
                    result.locker.wait();
                }
            }
            System.out.println(result.sum);
        }
    }

    代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
     
    public class Text1 {
     
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Callable<Integer> callable = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i <=1000; i++){
                        sum += i;
                    }
                    return sum;
                }
            };
            //由于Thread不能直接传一个callable实例,就需要一个辅助类来包装
            FutureTask<Integer> futureTask = new FutureTask<>(callable);
            Thread t = new Thread(futureTask);
            t.start();
            //尝试在主线程获取结果
            //如果FutureTask中的结果还没生成。此时就会阻塞等待
            //一直等到最终的线程把这个结果算出来,get返回
            Integer result = futureTask.get();
            System.out.println(result);
        }
    }

    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