Distributed lock is a way to control the synchronous access of shared resources between distributed systems.
In the distributed system, they often need to coordinate their actions. If different systems or different hosts of the same system share one or a group of resources, when accessing these resources, they often need to be mutually exclusive to prevent mutual interference to ensure consistency. In this case, they need to use distributed locks.
SETNX is short for SET if Not eXists. Command format: SETNX key value; use: set the key value to value only when the 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, set the value of key key to value, and return the old value of key before being set. Return value: if the key has no old value, that is, the key does not exist before it is set, the command returns nil. The command returns an error when the key key exists but is not of string type.
expire command format: exit key seconds, use: to set the lifetime for the given key. When the key expires (the lifetime is 0), it will be automatically deleted. Return value: set to return 1 successfully. When the key does not exist or cannot be set for the key's lifetime (for example, you try to update the key's lifetime in Redis under version 2.1.3), 0 is returned.
Del command format: DEL key [key ], use: delete one or more given keys. Keys that do not exist will be ignored. Return value: the number of deleted keys.
Redis implements the principle of distributed lock:
1. It is implemented through setnx (lock ﹣ timeout). If the lock is set to return 1, if there is no value set to return 0 successfully
2. Deadlock problem: judge whether it is overdue through practice. If it has expired, get the expiration time get(lockKey), and then get set (lock'timeout) judge whether it is the same as get. If it is the same, it proves that it has been locked successfully. Because it may cause multiple threads to execute getset (lock'timeout) method at the same time, which may cause multiple threads to only need to get set. For the thread that judges the locking success, Add the expire (lockkey, lock ﹣ timeout, timeunit. Milliseconds) expiration time to prevent multiple threads from overlapping time at the same time, resulting in the doubling of the lock aging time
Code:
/** * @author yaoxin * @date 2018/8/13 5:04 p.m. */ 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);// Specify connection type conn = DriverManager.getConnection(url, user, password);// Get connection statement = conn.createStatement();// Prepare to execute statement 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() + "Grab the lock 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("Update success" + Thread.currentThread().getName() + " Inventory surplus:" + (count - 1)); System.out.println(Thread.currentThread().getName() + " === > >Start unlocking"); boolean unlock = unlock(jedis, lock); if (unlock) System.out.println(Thread.currentThread().getName() + " === > >Unlock success"); } else { System.out.println("Update failed" + 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(); } } } } }, "thread" + 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 operation results are as follows:
For more information, please visit: