Dark Horse Comments 05 Distributed Lock 1 Mutex Lock and Expiration Time

Practical Chapter-09. Distributed Lock-Basic Principles and Comparison of Different Implementation Methods_bilibili_bilibili

1. Distributed lock

Because the sychonized lock inside the jvm cannot share the lock monitor between different jvm, a lock outside the jvm is needed to share.

2.redis setnx mutex lock

Just lock and unlock

2.1 Failure to release the lock may result in deadlock

Redis's setnx will not automatically release the lock. If the service goes down after locking, the lock may not be released and it may be deadlocked.

Therefore, you need to add an expiration time to the lock.

2.2 Ensure the atomicity of locking and expiration times

Use set + parameters to set the lock and expiration time at the same time to ensure that there will be no deadlock due to downtime before the expiration time is set.

Final version:

So far, the distributed lock has been basically completed, but it can still be improved.

2.3. Are other threads blocked after failure?

Generally, non-blocking methods are used , while blocking methods waste CPU and are troublesome to implement.

The blocking method means that if you find someone else using the lock, you will keep waiting.

Non-blocking means that if someone else takes the lock, I will return.

3. Implement redis set nx distributed lock 

 

3.1 Obtain redis distributed lock

private String name; //业务名字
    private StringRedisTemplate stringRedisTemplate;

    private static final String KEY_PREFIX = "lock:"; //规范名字
    private static final String ID_PREFIX = UUID.randomUUID() + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁 set  key value  NX  EX 过期时间
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);  //防拆箱空指针
    }

3.2 Release the redis distributed lock

4. Business uses redis distributed locks

In the order creation business, change the sychnoized lock to a distributed lock implemented by yourself (acquisition + unlocking)

5. Service blocking leads to the problem of accidental deletion of distributed locks (determining the thread identification of the lock)

Business 1 is blocked for too long, causing the lock to expire and be automatically deleted.

5.1 Solution: To determine whether the thread identifier is your own, you need a globally unique thread identifier.

 The thread number inside each jvm is an increasing number, but thread numbers may conflict between different jvm, so you need to find a way to distinguish threads not only within the jvm but also between jvm.

Uuid is a unique identification code that can ensure that the uuid of different services (jvm) must be different.

So use uuid + jvm internal thread id to uniquely identify threads in all jvm

Popular science: Introduction and use of Universal Unique Identifier UUID - Zhihu (zhihu.com)

5.1.1 Implementation (unique thread identification judgment is added when locking and releasing)

5.2 Another problem of accidental deletion (the lock is blocked and lost after the judgment is made, and then released later. It is necessary to ensure the atomicity of the thread ID and the released lock)

Practical combat chapter-15. Distributed lock-Lua script solves the atomicity problem of multiple commands_bilibili_bilibili

5.2.1 Guarantee atomicity--lua script

Call the call function provided by redis and pass in the redis command parameters

After leaving the parameter bits blank in order to pass parameters:

 5.2.2java calls lua script

 Read the Lua file in advance to avoid frequent reading and call it later.

In order to maintain the atomicity of determining the thread ID and releasing the lock when releasing the lock, rewrite the unlcok method.

6.Final code of this type

package com.hmdp.utils;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class SimpleRedisLock implements ILock {

    private String name; //业务名字
    private StringRedisTemplate stringRedisTemplate;

    private static final String KEY_PREFIX = "lock:"; //规范名字
    private static final String ID_PREFIX = UUID.randomUUID() + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁 set  key value  NX  EX 过期时间
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);  //防拆箱空指针
    }

    @Override
    public void unlock() {
        //调用lua脚本
        stringRedisTemplate.execute(UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),
                ID_PREFIX + Thread.currentThread().getId()
        );
    }

    /*
    *
    @Override
    public void unLock() {
        //获取线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁中的标识
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        //判断标识是否一致
        if (threadId.equals(id)) {
            //释放锁
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
    * */
}

Guess you like

Origin blog.csdn.net/m0_50973548/article/details/135020579