Maison >base de données >tutoriel mysql >Quels sont les points de connaissances liés aux verrous Java et Mysql ?

Quels sont les points de connaissances liés aux verrous Java et Mysql ?

王林
王林avant
2023-05-27 10:18:171103parcourir

    Définition du verrou

    Dans les programmes informatiques, les verrous sont utilisés pour des ressources exclusives. Ce n'est que lorsque le verrou est obtenu que le verrou peut être utilisé. les ressources correspondantes soient exploitées.

    Mise en place du verrouillage

    La mise en œuvre du verrouillage en bas de l'ordinateur s'appuie sur les instructions CAS (compare et swsp) fournies par le CPU. Pour une adresse mémoire, il. comparera la valeur d'origine et essaiera de modifier la valeur, si la valeur est modifiée avec succès indique si le verrou a été saisi.

    Verrous dans JVM

    Il existe 2 verrous couramment utilisés dans jvm

    synchronisé

    synchronisé est fourni par java Le le verrouillage par mot-clé peut verrouiller des objets, des classes et des méthodes.
    Après JDK1.6, la synchronisation a été optimisée et le verrouillage biaisé et le mode de verrouillage léger ont été ajoutés. Désormais, la logique de fonctionnement du verrouillage synchronisé est la suivante :

    • #🎜🎜. # Lors du verrouillage initial, un verrou de biais sera ajouté, c'est-à-dire "

      biaise vers le fil qui a acquis le verrou en dernier lieu ". Ce mode améliore considérablement le débit d'un seul thread acquérant à plusieurs reprises le même verrou. Selon les responsables de Java, la plupart des conflits de verrouillage se produisent sur le même thread.

    • Si l'acquisition CAS du verrouillage de biais échoue, cela signifie que le thread actuel est différent du fil polarisé par le verrouillage de biais, et le verrouillage de biais sera

      mise à niveau vers un verrou léger, la caractéristique d'un verrou léger est d'obtenir le verrou via spinCAS.

    • Si l'acquisition du spin échoue, le verrou sera mis à niveau vers un verrou de poids et tous les threads en attente du verrou seront suspendus par la JVM une fois le verrou terminé. libéré, la JVM se réveillera avec une notification unifiée et tentera à nouveau le verrouillage CAS. En cas d'échec, continuez à se bloquer.

    De toute évidence, le but de la conception du verrou biaisé est que "du point de vue officiel de Java, la plupart des conflits pour le même verrou se produisent sur le même thread".

    Le but de la conception légère des verrous est "à court terme, la contention des verrous peut être obtenue via spin CAS, et la consommation de rotation du processeur sur une courte période de temps est inférieure à la consommation de suspension et de réveil des threads."
    Le verrouillage du poids est la logique synchronisée avant l'optimisation initiale.

    ReentrantLock

    En parlant de ReentrantLock, il faut parler d'AQS dans JUC.

    Le nom complet d'AQS est AbstractQueueSynchronizer. Presque toutes les classes d'outils de JUC s'appuient sur AQS pour l'implémentation.
    AQS est une classe abstraite en Java, mais c'est essentiellement l'implémentation d'une idée en Java.
    La logique de mise en œuvre d'AQS est la suivante :

    • Construire une file d'attente

    • Maintenance dans la file d'attente nécessite d'attendre Le thread de verrouillage

    • Le nœud principal est toujours le nœud détenant le verrou (ou détenant la ressource), et les nœuds en attente sont connectés en séquence après le nœud principal.

    • Une fois que le nœud principal a libéré le verrou, il réveillera les nœuds en attente dans l'ordre, puis ces nœuds tenteront à nouveau d'acquérir le verrou.

    Après l'optimisation du verrouillage synchronisé, l'essence d'AQS n'est pas très différente de synchronisée, et les performances des deux ne sont pas très différentes, donc les caractéristiques actuelles d'AQS Oui :

    • est un verrou implémenté au niveau de l'API Java, donc diverses classes d'outils simultanées peuvent être implémentées et le fonctionnement est plus flexible

      #🎜🎜 ##🎜 🎜#

      Parce qu'il fournit des mécanismes tels que le délai d'attente et un fonctionnement flexible, il n'est pas sujet aux blocages. Si un blocage se produit, il sera plus difficile à résoudre car jstack n'affichera pas d'indicateur de blocage.
    • peut obtenir un verrouillage équitable, mais synchronisé doit être un verrouillage injuste.
    • Comme il s'agit d'un verrou implémenté par la couche JavaApi, il peut répondre aux interruptions.
    • À ce stade, vous constaterez que ReentrantLock peut en fait être considéré comme l'implémentation de synchronisé dans la couche JavaApi.
    Verrou MySQL

    Verrou partagé (S) et verrou exclusif (X)

    Scope

    Ces deux Tous les types des verrous incluent des verrous au niveau des lignes et des verrous au niveau des tables.

    Lors de l'acquisition d'un verrou partagé, si les données sont verrouillées par le verrou exclusif d'une autre transaction, elles ne peuvent pas être acquises et doivent attendre que le verrou exclusif soit libéré.

    Verrouillage d'intention

    Scope

    Le verrouillage d'intention est un verrouillage de table

    . vérifiera certainement le verrouillage de l'intention.

    Le protocole de verrouillage d'intention est le suivant :

    Avant qu'une transaction puisse obtenir un verrou partagé sur une ligne de la table, elle doit d'abord obtenir un verrou IS ou un verrou plus fort sur la table.

    Avant qu'une transaction puisse obtenir un verrou exclusif sur une ligne de la table, elle doit d'abord obtenir un verrou IX sur la table.

    Avant d'acquérir un verrou partagé ou exclusif sur un verrou de table, le verrou partagé sur la table doit être vérifié.

    Les règles d'exclusion mutuelle pour les verrous de table et les verrous d'intention sont les suivantes :

    X IX S IS

    X Conflict Conflict Conflict Conflict

    IX Conflict Compatible Conflict Compatible#🎜 🎜#S Conflict Conflict Compatible Compatible
    IS Conflict Compatible Compatible Compatible


    Le but du verrouillage d'intention est le suivant : lors de l'acquisition d'un verrouillage de table, vous pouvez utiliser le verrouillage d'intention pour rapidement déterminer s'il peut être obtenu.

    Parce que lors de l'acquisition d'un verrou au niveau de la ligne, le verrou d'intention correspondant sera acquis en premier, afin que les autres transactions puissent rapidement juger du verrou d'intention lors de l'acquisition des verrous de table, sans avoir besoin de chaque ligne va scanner. Une attention particulière est que les verrous d'intention peuvent être superposés, c'est-à-dire qu'il y en aura plusieurs. Par exemple, la transaction T1 acquiert le verrou d'intention IX1 et le verrou de niveau ligne X1, et la transaction T2 peut. acquérez toujours le verrou d'intention IX2 et le verrou de niveau ligne X1, de sorte que le verrou d'intention n'est vérifié qu'avant d'acquérir le verrou de niveau table.

    Verrouillage d'enregistrement

    Le verrouillage d'enregistrement prend effet sur l'index pour protéger les données de ligne contre la modification par d'autres transactions lorsque SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE.

    Le verrouillage d'enregistrement prendra toujours effet lorsqu'il n'y a pas d'index, car innodb créera un index caché pour chaque table.

    Le verrouillage d'enregistrement est le verrouillage de ligne le plus basique.

    Verrouillage des espaces

    Le verrouillage des espaces prend effet sur l'index. Il est utilisé pour verrouiller la ligne après la valeur de l'index pour empêcher l'insertion. Il prendra effet lors de la sélection à partir de la table où index=? , alors index= sera verrouillé. 1Index lignes liées au nœud, empêcher d'autres transactions d'insérer des données.

    Mais cela n'empêchera pas l'instruction de mise à jour, même si les données de mise à jour n'existent pas.

    Next-Key Locks

    Ce verrou est une combinaison de verrouillage d'enregistrement et de verrouillage d'espacement. En bref, lors de la sélection dans la table où index=? pour la mise à jour, il y aura un verrouillage d'espacement pour empêcher l'insertion et le verrouillage d'enregistrement dans Empêcher la mise à jour. et suppression de cette donnée sur l'index. Cette clé suivante n'est qu'une généralisation de ces deux verrous, car ces deux verrous apparaissent généralement ensemble lors de la sélection pour la mise à jour.

    Insérer des verrous d'intention

    Insérez des verrous d'intention, similaires aux verrous d'intention. Il s'agit d'un verrouillage d'espacement spécial, qui ne se produit pas lors de la sélection pour la mise à jour, mais se produit lorsque l'insertion se produit en même temps. Par exemple, lorsque deux transactions insèrent la plage d'index [4,7] en même temps, elles obtiennent l'intention. verrouillage de la plage en même temps. Ceci Lorsque la transaction est bloquée, par exemple, A : insert-5, B : insert-7, les deux transactions ne seront pas bloquées pour le moment.

    Le verrouillage d'intention d'insertion est un verrouillage d'espacement spécial, conçu pour empêcher le blocage fréquent de l'insert dans des intervalles de verrouillage d'espacement normaux, tels que A : insert-5, B : insert-7, si aucun verrou d'intention n'est inséré, alors les deux. 5 et 7 tenteront d'acquérir le verrou d'espacement. À ce moment, la deuxième transaction sera bloquée. Cependant, en insérant le verrou d'intention, la deuxième transaction ne sera pas bloquée. bloc.

    Verrouillage AUTO-INC

    Verrouillage à incrémentation automatique, ce verrou est évidemment un verrou à insertion au niveau de la table, afin de garantir que la clé primaire de la table avec clé primaire à incrémentation automatique maintient l'auto-incrémentation atomique.

    En ce qui concerne les serrures, chacun devrait mieux comprendre les principes et les modèles des différentes conceptions et opérations de serrures, afin qu'après avoir approfondi sa compréhension, il puisse les utiliser de manière plus approfondie et approfondie.

    Scénarios et utilisations courants des verrous

    double vérification

    Comme nous le savons tous, les transactions MySQL ne sont d'aucune utilité pour empêcher les insertions répétées, et les index uniques présentent de nombreux défauts. Il est préférable de ne pas les utiliser en entreprise, donc de manière générale. , pour éviter la duplication. La manière courante d'insérer consiste à utiliser des verrous distribués, qui est une méthode d'écriture plus courante.

    final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
    if (weekendNoticeReadCountDO == null) {
        final String lockKey = RedisConstant.LOCK_WEEKEND_READ_COUNT_INSERT + ":" + noticeRequestDTO.getNoticeId();
        ClusterLock lock = clusterLockFactory.getClusterLockRedis(
            RedisConstant.REDIS_KEY_PREFIX,
            lockKey
        );
        if (lock.acquire(RedisConstant.REDIS_LOCK_DEFAULT_TIMEOUT)) {
            //double check
            final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
            if (weekendNoticeReadCountDO == null) {
                try {
                    lock.execute(() -> {
                        WeekendNoticeReadCountDO readCountDO = new WeekendNoticeReadCountDO();
                        readCountDO.setNoticeId(noticeRequestDTO.getNoticeId());
                        readCountDO.setReadCount(1L);
                        readCountDO.setCreateTime(new Date());
                        readCountDO.setUpdateTime(new Date());
                        weekendNoticeReadRepositoryService.insert(readCountDO);
                        return true;
                    });
                } catch (ApiException err) {
                    throw err;
                } catch (Exception e) {
                    log.error("插入", e);
                    throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服务端出错");
                }
            } else {
                weekendNoticeReadRepositoryService.noticeCountAdd(weekendNoticeReadCountDO);
            }
        } else {
            log.warn("redis锁获取超时,key:{}", lockKey);
            throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服务器繁忙,请稍后重试");
        }
    }

    Après l'acquisition du verrou, il peut être acquis après avoir attendu. À ce moment, le thread précédent qui a libéré le verrou peut avoir inséré des données, donc à l'intérieur du verrou, il est encore nécessaire de vérifier si les données existent à nouveau.
    Cette méthode d'écriture convient à la plupart des scénarios d'écriture qui nécessitent un caractère unique.

    Éviter les impasses

    Comment éviter les impasses ? La méthode la plus simple et la plus efficace est la suivante : **Ne mettez pas le cadenas à l'intérieur de la serrure. En bref, il est préférable d'utiliser le cadenas seul, pas comme une poupée gigogne.
    Faites également attention à certains verrous implicites, comme les bases de données.
    Transaction A :

    • Insérez [5,7] et insérez le verrou d'intention.

    • sélectionnez la mise à jour [100,150], verrouillage de l'espace.
      Transaction B :

    • sélectionnez la mise à jour [90 120], verrouillage de l'espace.

    • Insérez [4,6] et insérez le verrou d'intention.

    À ce moment-là, dans un scénario concurrent, il peut arriver que A détienne le verrou d'écart de [5,7] et attend le verrou d'écart de la transaction B [90,120]. conduit à une impasse.
    **

    Au fait, parlons des problèmes courants dans les scénarios de concurrence

    Confusion en lecture et en écriture

    Lors de l'écriture de code métier et de la définition de certaines classes d'outils ou de classes de cache, il est facile d'être négligent et de provoquer des problèmes similaires.
    Par exemple, lors de la création d'un cache statique, des méthodes telles que putIfAbsent dans ConcurrentHashMap ne sont pas utilisées et aucun verrou n'est utilisé pour le construire. Par conséquent, le thread ci-dessous est supprimé dès que le thread ci-dessus le met, ou le cache. est construit deux fois.

    Redis ou certaines opérations concurrentes libèrent des verrous ou des ressources sans vérifier si elles sont détenues par le thread actuel

    Ceci est également mentionné dans l'exemple de code du verrou Redis.
    Le thread A acquiert le verrou. À ce moment, B et C attendent. Ensuite, le temps d'exécution de A est trop long, ce qui entraîne l'expiration du verrou et est automatiquement libéré. ​​À ce moment, B acquiert le verrou et s'exécute joyeusement. . Ensuite, une fois que A a terminé l'exécution, le verrou est libéré. ​​À ce moment-là, il n'a pas été jugé s'il était toujours détenu par lui-même, ce qui a entraîné la suppression du verrou détenu par B. À ce moment-là, C a de nouveau acquis le verrou. BC a été exécuté au même moment.

    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