#Distributed lock is a way to control synchronous access to shared resources between distributed systems.
In distributed systems, it is often necessary to coordinate their actions. If different systems or different hosts of the same system share one or a group of resources, then when accessing these resources, mutual exclusion is often required to prevent mutual interference and ensure consistency. In this case, it is necessary to Distributed locks are used.
Use the four redis commands setnx, getset, expire, and del to implement (Recommended learning: Redis video tutorial)
setnx is 『SET Abbreviation for "if Not eXists" (if it does not exist, then SET). Command format: SETNX key value; Usage: Set the value of key key to value only when key key does not exist. If the key key already exists, the SETNX command does nothing. Return value: The command returns 1 when the setting is successful and 0 when the setting fails.
getset command format: GETSET key value, sets the value of key key to value and returns the old value of key key before it was set. Return value: If the key key has no old value, that is, the key key does not exist before it is set, then the command returns nil. When the key key exists but is not of type string, the command returns an error.
expire command format: EXPIRE key seconds, usage: set the survival time for a given key. When the key expires (the survival time is 0), it will be automatically deleted. Return value: 1 is returned if the setting is successful. When the key does not exist or the lifetime cannot be set for the key (for example, if you try to update the lifetime of the key in a version of Redis earlier than 2.1.3), 0 is returned.
del command format: DEL key [key...], usage: delete one or more given keys, non-existing keys will be ignored. Return value: The number of deleted keys.
The principle of Redis implementing distributed locks:
1. Implemented through setnx(lock_timeout), if the lock is set, it returns 1, if there is already a value, it returns 0 if it is not set successfully.
2. Deadlock problem: judge whether it has expired through practice. If it has expired, get the expiration time get(lockKey), and then getset(lock_timeout) to judge whether it is the same as get. If it is the same, it proves that the lock has been successfully locked. , because it may cause multiple threads to execute the getset(lock_timeout) method at the same time, which may cause multiple threads to only need to getset. For the thread that determines the lock is successful, add expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS) expiration time to prevent multiple Threads superimpose time at the same time, causing the lock aging time to double
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; } }
The running results are as follows:
For more Redis related technical articles, please visit the Redis Getting Started Tutorial column to learn!
The above is the detailed content of How to implement the principle of redis distributed lock. For more information, please follow other related articles on the PHP Chinese website!