分散式鎖

在鍛鍊的新生代農民工 發表於 2022-06-15

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();
        }
    }
}

複製程式碼

分散式鎖

相關文章