Maison > Java > javaDidacticiel > le corps du texte

Quels sont les pièges courants de ReentrantLock en Java ?

王林
Libérer: 2023-05-10 18:55:14
avant
1557 Les gens l'ont consulté

Lock Introduction

Lock est une interface de niveau supérieur, et toutes ses méthodes sont présentées ci-dessous :

Quels sont les pièges courants de ReentrantLock en Java ?

Sa liste de sous-classes est la suivante :

Quels sont les pièges courants de ReentrantLock en Java ?

Nous utilisons généralement ReentrantLock pour définir son instances , La relation entre eux est illustrée dans la figure ci-dessous :

Quels sont les pièges courants de ReentrantLock en Java ?

PS : Sync signifie verrouillage de synchronisation, FairSync signifie verrouillage équitable et NonfairSync signifie verrouillage injuste.

Utilisation de ReentrantLock

L'apprentissage de toute compétence commence par son utilisation, nous ne faisons donc pas exception. Jetons d'abord un coup d'œil à l'utilisation de base de ReentrantLock :

public class LockExample {
    // 创建锁对象
    private final ReentrantLock lock = new ReentrantLock();
    public void method() {
        // 加锁操作
        lock.lock();
        try {
            // 业务代码......
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}
Copier après la connexion

Après la création de ReentrantLock, il y a deux points clés :

  • Opération de verrouillage : lock()

  • Opération de verrouillage : unlock()

Pièges dans ReentrantLock

1. ReentrantLock est par défaut un verrouillage injuste

Beaucoup de gens penseront que (surtout pour les amis novices) , l'implémentation par défaut de ReentrantLock est un verrou équitable, mais ce n'est pas le cas. ReentrantLock est un verrou injuste par défaut (cela est principalement dû à des considérations de performances),

Par exemple, le code suivant :

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 定义线程任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // 加锁
                lock.lock();
                try {
                    // 打印执行线程的名字
                    System.out.println("线程:" + Thread.currentThread().getName());
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        };
        // 创建多个线程
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
Copier après la connexion

. Les résultats d'exécution du programme ci-dessus sont les suivants :

Quels sont les pièges courants de ReentrantLock en Java ?

D'après les résultats d'exécution ci-dessus, nous pouvons voir que ReentrantLock est un verrou injuste par défaut. Étant donné que les noms des threads sont incrémentés en fonction de l'ordre dans lequel ils sont créés, s'il s'agit d'un verrou équitable, alors l'exécution des threads devrait augmenter dans l'ordre. Cependant, comme le montrent les résultats ci-dessus, l'exécution et l'impression de. les threads sont dans le désordre. Cette description ReentrantLock est un verrou injuste par défaut.

Il est également très simple de définir ReentrantLock comme un verrou équitable. Il vous suffit de définir un véritable paramètre de construction lors de la création de ReentrantLock Comme le montre le code suivant :

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象(公平锁)
    private static final ReentrantLock lock = new ReentrantLock(true);
    public static void main(String[] args) {
        // 定义线程任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // 加锁
                lock.lock();
                try {
                    // 打印执行线程的名字
                    System.out.println("线程:" + Thread.currentThread().getName());
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        };
        // 创建多个线程
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
Copier après la connexion

Les résultats d'exécution du programme ci-dessus sont les suivants. suit :

Quels sont les pièges courants de ReentrantLock en Java ?

Comme le montrent les résultats ci-dessus, lorsque nous définissons explicitement le véritable paramètre de construction pour ReentrantLock, ReentrantLock devient un verrou équitable et l'ordre dans lequel les threads acquièrent les verrous devient ordonné.

En fait, à partir du code source de ReentrantLock, nous pouvons également voir s'il s'agit d'un verrou équitable ou injuste. Une partie du code source de ReentrantLock est implémentée comme suit :

 public ReentrantLock() {
     sync = new NonfairSync();
 }
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
Copier après la connexion

Comme on peut le voir sur le. au-dessus du code source, ReentrantLock créera un verrou injuste par défaut Lock, si la valeur du paramètre de construction est explicitement définie sur true lors de sa création, il créera un verrou équitable.

2. Relâchez enfin le verrou

Lorsque vous utilisez ReentrantLock, vous devez vous rappeler de libérer le verrou, sinon le verrou sera occupé tout le temps et les autres threads utilisant le verrou attendront éternellement, nous utilisons donc ReentrantLock Ce faisant, assurez-vous de déverrouiller enfin le verrou, afin de pouvoir être sûr que le verrou sera libéré.

Contre exemple

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();
        System.out.println("Hello,ReentrantLock.");
        // 此处会报异常,导致锁不能正常释放
        int number = 1 / 0;
        // 释放锁
        lock.unlock();
        System.out.println("锁释放成功!");
    }
}
Copier après la connexion

Le résultat de l'exécution du programme ci-dessus est le suivant :

Quels sont les pièges courants de ReentrantLock en Java ?

À partir des résultats ci-dessus, on peut voir que lorsqu'une exception se produit, le verrou n'est pas libéré normalement, ce qui les autres threads utilisant le verrou seront en permanence dans un état d'attente.

Exemple positif

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();
        try {
            System.out.println("Hello,ReentrantLock.");
            // 此处会报异常
            int number = 1 / 0;
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释放成功!");
        }
    }
}
Copier après la connexion

Les résultats d'exécution du programme ci-dessus sont les suivants :

Quels sont les pièges courants de ReentrantLock en Java ?

À partir des résultats ci-dessus, on peut voir que bien qu'une exception se produise dans la méthode, elle n'affecte pas la libérer l'opération du verrou ReentrantLock, afin que les autres threads utilisant ce verrou puissent obtenir et s'exécuter normalement.

3. Le verrou ne peut pas être libéré plusieurs fois

Le nombre d'opérations de verrouillage et le nombre d'opérations de déverrouillage doivent correspondre un à un, et un verrou ne peut pas être libéré plusieurs fois, car cela entraînerait le signalement d'une erreur par le programme.

Contre-exemple

Un verrou correspond à deux opérations de déverrouillage, ce qui amène le programme à signaler une erreur et à mettre fin à l'exécution. L'exemple de code est le suivant :

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();

        // 第一次释放锁
        try {
            System.out.println("执行业务 1~");
            // 业务代码 1......
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }

        // 第二次释放锁
        try {
            System.out.println("执行业务 2~");
            // 业务代码 2......
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }
        // 最后的打印操作
        System.out.println("程序执行完成.");
    }
}
Copier après la connexion

Les résultats d'exécution du programme ci-dessus sont les suivants. :

Quels sont les pièges courants de ReentrantLock en Java ?

À partir des résultats ci-dessus, vous pouvez voir que lorsque le deuxième déverrouillage est exécuté, le programme signale une erreur et termine l'exécution, ce qui empêche le code après l'exception de s'exécuter normalement.

4.lock 不要放在 try 代码内

在使用 ReentrantLock 时,需要注意不要将加锁操作放在 try 代码中,这样会导致未加锁成功就执行了释放锁的操作,从而导致程序执行异常。

反例

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        try {
            // 此处异常
            int num = 1 / 0;
            // 加锁操作
            lock.lock();
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }
        System.out.println("程序执行完成.");
    }
}
Copier après la connexion

以上程序的执行结果如下: 

Quels sont les pièges courants de ReentrantLock en Java ?

 从上述结果可以看出,如果将加锁操作放在 try 代码中,可能会导致两个问题:

  • 未加锁成功就执行了释放锁的操作,从而导致了新的异常;

  • 释放锁的异常会覆盖程序原有的异常,从而增加了排查问题的难度。

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!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!