Le verrouillage distribué est un moyen de contrôler l'accès synchrone aux ressources partagées entre les systèmes distribués.
Dans les systèmes distribués, il est souvent nécessaire de coordonner leurs actions. Si différents systèmes ou différents hôtes du même système partagent une ou un groupe de ressources, alors lors de l'accès à ces ressources, une exclusion mutuelle est souvent nécessaire pour éviter les interférences mutuelles et garantir la cohérence. Dans ce cas, il est nécessaire d'utiliser des verrous distribués.
Utilisez les quatre commandes Redis setnx, getset, expire et del pour implémenter (Apprentissage recommandé : Tutoriel vidéo Redis)
setnx est 『 SET Abréviation de "if Not eXists" (s'il n'existe pas, alors SET). Format de commande : valeur de clé SETNX ; Utilisation : définissez la valeur de clé clé sur valeur uniquement lorsque la clé clé n'existe pas. Si la clé clé existe déjà, la commande SETNX ne fait rien. Valeur de retour : la commande renvoie 1 lorsque le paramètre est réussi et 0 lorsque le paramètre échoue.
format de commande getset : GETSET key value, définit la valeur de key key sur value et renvoie l'ancienne valeur de key key avant qu'elle ne soit définie. Valeur de retour : si la clé clé n'a pas d'ancienne valeur, c'est-à-dire que la clé clé n'existe pas avant d'être définie, alors la commande renvoie nil. Lorsque la clé clé existe mais n'est pas de type chaîne, la commande renvoie une erreur.
format de commande expire : EXPIRE key seconds, utilisation : définir le temps de survie pour une clé donnée Lorsque la clé expire (le temps de survie est de 0), elle sera automatiquement supprimée. Valeur de retour : 1 est renvoyé si le réglage est réussi. Lorsque la clé n'existe pas ou que la durée de vie ne peut pas être définie pour la clé (par exemple, si vous essayez de mettre à jour la durée de vie de la clé dans une version de Redis antérieure à 2.1.3), 0 est renvoyé.
format de la commande del : touche DEL [clé...], utilisation : supprimer une ou plusieurs clés données, les clés inexistantes seront ignorées. Valeur de retour : le nombre de clés supprimées.
Le principe de Redis implémentant des verrous distribués :
1 Implémenté via setnx(lock_timeout), si le verrou est défini, il renvoie 1, s'il y en a déjà un. valeur, il renvoie 0 s'il n'est pas défini avec succès.
2. Problème de blocage : déterminez s'il a expiré par la pratique. S'il a expiré, obtenez le délai d'expiration get(lockKey), puis getset(lock_timeout. ) pour déterminer si c'est la même chose que get. Si c'est la même chose, cela prouve que le verrou a été verrouillé avec succès, car cela peut amener plusieurs threads à exécuter la méthode getset(lock_timeout) en même temps, ce qui peut provoquer. plusieurs threads n'ont besoin que d'être définis. Pour le thread qui détermine que le verrouillage est réussi, ajoutez le délai d'expiration expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS) pour empêcher plusieurs threads de superposer le temps en même temps, ce qui entraînerait un doublement du temps de vieillissement du verrouillage
Code :
/** * @author yaoxin * @date 2018/8/13下午5:04 */ public class RedisLockTest { public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8"; public static final String name = "com.mysql.jdbc.Driver"; public static final String user = "root"; public static final String password = ""; public static void main(String[] args) { Integer count = 50; while (count > 0) { count--; new Thread(new Runnable() { @Override public void run() { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.auth("1234"); String lock = lock(jedis); if (lock != null) { Statement statement = null; Connection conn = null; ResultSet resultSet = null; try { Class.forName(name);// 指定连接类型 conn = DriverManager.getConnection(url, user, password);// 获取连接 statement = conn.createStatement();// 准备执行语句 String querySql = "SELECT id,name,count FROM production WHERE id=2"; resultSet = statement.executeQuery(querySql); int count = 0; while (resultSet.next()) { System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id") + " name: " + resultSet.getString("name") + " count: " + resultSet.getString("count")); count = Integer.valueOf(resultSet.getString("count")); } String updateSql = "UPDATE production SET count=" + (count - 1) + " WHERE id=2"; int rows = statement.executeUpdate(updateSql); if (rows > 0) { System.out.println("更新成功" + Thread.currentThread().getName() + " 库存剩余:" + (count - 1)); System.out.println(Thread.currentThread().getName() + " === > >开始解锁"); boolean unlock = unlock(jedis, lock); if (unlock) System.out.println(Thread.currentThread().getName() + " === > >解锁成功"); } else { System.out.println("更新失败" + Thread.currentThread().getName()); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (conn != null) conn.close(); if (statement != null) statement.close(); if (resultSet != null) resultSet.close(); } catch (Exception e) { e.printStackTrace(); } } } } }, "线程" + count).start(); } } public static String lock(Jedis jedis) { try { while (true) { String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + ""; if (jedis.setnx("lock", lockTime) == 1) { jedis.expire("lock", 5); return lockTime; } String lock = jedis.get("lock"); if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) { String oldLockTime = jedis.getSet("lock", lockTime); if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) { return lockTime; } } Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } return null; } public static boolean unlock(Jedis jedis, String lockTag) { if (lockTag.equals(jedis.get("lock"))) { jedis.del("lock"); return true; } return false; } }
Le résultat en cours d'exécution est le suivant :
Pour plus d'articles techniques liés à Redis, veuillez visiter la colonne Tutoriel de démarrage de Redis pour apprendre !
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!