Home  >  Article  >  Java  >  Java implements distributed lock based on Redis

Java implements distributed lock based on Redis

(*-*)浩
(*-*)浩forward
2019-08-28 16:12:442536browse

Distributed locks can be implemented in many ways, such as zookeeper, redis.... Either way, the basic principle remains the same: a state value is used to represent the lock, and the occupation and release of the lock are identified by the state value.

Java implements distributed lock based on Redis

1. Why Redis can easily implement distributed locks

1. Redis is a single-process single-thread mode, using queues The mode turns concurrent access into serial access, and there is no competition between multiple clients' connections to Redis.

2. Redis’s SETNX command can easily implement distributed locks.

setNX(SET if Not eXists)

Syntax: SETNX key value

Return value: If the setting is successful, 1 is returned; if the setting fails, 0 is returned.

Set the value of key to value if and only if key does not exist, and return 1; if the given key already exists, SETNX does not take any action and returns 0.

To sum up, you can use the return value of setnx to determine whether the lock is acquired, and you don’t have to worry about concurrent access, because Redis is single-threaded, so if it returns 1, the lock is acquired, and 0 is returned. It was not obtained. When the business operation is completed, the lock must be released. The logic of releasing the lock is very simple, which is to delete the previously set key, so that the lock can be obtained by setnx the key next time.

2. Distributed lock implementation

We already know that distributed locks can be implemented through Redis’s own function setNX. The specific implementation steps are as follows.

I installed the Redis service in a CentOS7 Linux virtual machine. The IP address is: 192.168.246.130 and the service port is: 6379.

The following is an example of java implementing distributed locks through redis:

import redis.clients.jedis.Jedis;
public class RedisLock {
	//锁的key
	private static final String key = "DistributedRedisLock";
	private static Integer count = 0;
	public static void main(String[] args) {
		for(int i=0;i<1000;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					//获取Redis连接
					Jedis jedis = new Jedis("192.168.246.130", 6379);
					try{
						while(true){
							//获取锁
							if(jedis.setnx(key, Thread.currentThread().getName()) == 1){
								try{
									System.out.println("线程("+Thread.currentThread().getName()+")获取到锁,开始执行操作");
									count++;
									System.out.println(count);
									break;
								}finally{
									System.out.println("操作执行完成,释放锁");
									//操作执行完一定要释放锁,所以在finally块中执行
									jedis.del(key);
								}
							}else{
								//返回的不是1,说明已经有某个线程获取到了锁
								try {
									//等待100毫秒之后重试
									Thread.sleep(100l);
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
							}
						}
					}catch(Exception e){
						e.printStackTrace();
					}finally{
						//释放Redis连接
						jedis.disconnect();
					}
				}
			}).start();
		}
	}
}

The output of the above code is:

线程(Thread-320)获取到锁,开始执行操作
1
操作执行完成,释放锁
线程(Thread-463)获取到锁,开始执行操作
2
操作执行完成,释放锁
线程(Thread-997)获取到锁,开始执行操作
3
操作执行完成,释放锁
...
线程(Thread-409)获取到锁,开始执行操作
998
操作执行完成,释放锁
线程(Thread-742)获取到锁,开始执行操作
999
操作执行完成,释放锁
线程(Thread-286)获取到锁,开始执行操作
1000
操作执行完成,释放锁

Although the above code is in a single application with multiple threads It was tested, but even if multiple applications and multiple threads are used to acquire locks in a distributed environment, the results are still correct.

3. Solve the deadlock problem

The previous example code is just a test code, just to illustrate the principle. The example itself is very simple, so there are some ill-considered aspects. . For example, after acquiring the lock, an environmental problem occurs during the execution of the business operation and the connection to Redis is disconnected. Then the lock cannot be released in the finally block, causing other threads waiting to acquire the lock to wait indefinitely, which is what happens. Deadlock phenomenon.

Solution:

You can set an expiration time for the lock in Redis, so that even if the lock cannot be released, the lock can be automatically released after a period of time.

In terms of code, you only need to add the following code to the try statement block after acquiring the lock:

jedis.expire(key, 10); //这里给锁设置10秒的过期时间

A better solution:

One solution is not very good, because when the business operation processing time is very long and exceeds the set expiration time, the lock is automatically released, and then when the operation of releasing the lock in the finally block is executed, the lock may have been used by other users. The lock held by a thread will cause the lock held by other threads to be released, thus causing concurrency problems. Therefore, a more appropriate way is to determine whether the lock has expired when releasing the lock. If it has expired, there is no need to release it again.

In the code, change the operation after acquiring the lock to the following code:

long start = System.currentTimeMillis(); //获取起始时间毫秒数
try{
  jedis.expire(key, 10);
  ...
}finally{
  ...
  if(System.currentTimeMillis() < start+10*1000){
     //如果之前设置的锁还未过期,则释放掉
     jedis.del(key);
  }
}

The above is the detailed content of Java implements distributed lock based on Redis. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete