欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分布式锁

程序员文章站 2022-07-05 11:46:44
...

redis lock

单机lock

  • synchronized
  • reentrantLock
  • cas

分布式lock

  • redis lock
  • zookeeper lock
  • 数据库行锁

redis能称为分布式锁的条件

  • 互斥性
  • 原子性

redis加锁流程

踩过的坑

线程A获得redis的锁,执行业务发生超时释放锁,线程B获得线程A释放的锁,线程A业务执行结束,释放了线程B的锁 解决办法:每个线程获取锁,设置不同value值,线程释放锁的时候,将原先线程设置的value与当前锁的value做比较,相同时释放锁

优化的点

1、删除锁并非原子性操作

    public void unlock() {
        String value = convertValue();
        //1.获取lockValue
        String lockValue = RedisProxyUtil.getCacheInstance().getJedisReturn().get(name);
        //2.释放锁
        if (value.equalsIgnoreCase(lockValue)) {
            RedisProxyUtil.getCacheInstance().getJedisReturn().del(name);
        }
    }
复制代码

可能出现的情况 线程A同时出现超时和业务执行结束,那么线程A会出现两次释放锁,两次释放锁第一步都走完了,其中一个释放锁的线程1、2都走完了,导致线程B获得锁,线程A另一个线程进行步骤二释放了线程B的锁

解决方法:可以使用lua脚本进行原子删除操作

public void unlock() {
    String checkAndDelScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                                "return redis.call('del', KEYS[1]) " +
                                "else " +
                                "return 0 " +
                                "end";
    jedis.eval(checkAndDelScript, 1, lockKey, lockValue);
}
复制代码

2、自己实现redis锁不易维护,可以使用Redisson,话不多说,上代码

/**
 * @author jujunjun
 * @version v1.0.0
 * @date 2018/8/30
 */
public class RedissonTest {
    public static RedissonClient redisson;

    static {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://10.30.130.180:6379");
        redisson = Redisson.create(config);
    }

    public static void main(String[] args){

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 5; i++) {
            executorService.execute(()-> testLock());
        }
    }

    private static void testLock() {
        RLock rLock = redisson.getLock("junjunlock");
        try {
            /* 尝试加锁 */
            boolean lock = rLock.tryLock(10, 10, TimeUnit.SECONDS);
            if(lock) {
                System.out.println(Thread.currentThread().getName()+ "------" + System.currentTimeMillis());
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /* 释放锁 */
            rLock.unlock();
        }
    }
}
复制代码

zookeeper lock

流程

package com.code.web.service;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author jujunjun
 * @version v1.0.0
 * @date 2018/8/31
 */
public class ZookeeperLock {
    public static CuratorFramework client;
    static {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        client = CuratorFrameworkFactory.newClient("10.30.130.180:2181", retryPolicy);

    }

    public static void main(String[] args) throws Exception {
        client.start();
        InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 5; i++) {
            executorService.execute(()-> testLock(mutex));
        }
    }

    private static void testLock(InterProcessMutex mutex) {
        try {
            mutex.acquire();
            System.out.println(Thread.currentThread().getName()+ "------" + System.currentTimeMillis());
            Thread.sleep(2000);
            mutex.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

复制代码

转载于:https://juejin.im/post/5b88d2ac6fb9a019d67c1eed