JAVA基于redis實(shí)現(xiàn)的分布式鎖

redis分布式鎖

本文主要以Jedis客戶端為例

pom.xml文件

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

連接池配置類

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author oumiga
 */
@Configuration
public class JedisConfig {

    /**
     * 地址
     */
    @Value("${spring.jedis.host}")
    private String host;

    /**
     * 端口號(hào)
     */
    @Value("${spring.jedis.port}")
    private int port;

    /**
     * 密碼
     */
    @Value("${spring.jedis.password}")
    private String password;

    /**
     * 連接超時(shí)時(shí)間
     */
    @Value("${spring.jedis.timeOut}")
    private int timeOut;

    /**
     * 最大連接數(shù)
     */
    @Value("${spring.jedis.maxConnect}")
    private int maxConnect;

    /**
     * 等待獲取連接最大時(shí)間
     */
    @Value("${spring.jedis.maxWaitTime}")
    private long maxWaitTime;

    /**
     * 最大空閑連接數(shù)
     */
    @Value("${spring.jedis.maxFreeConnect}")
    private int maxFreeConnect;

    /**
     * 最小空閑連接數(shù)
     */
    @Value("${spring.jedis.minFreeConnect}")
    private int minFreeConnect;

    @Bean
    public JedisPool getJedisPoolFactory(){
        JedisPoolConfig config = new JedisPoolConfig();
        config.setBlockWhenExhausted(true);
        config.setMaxIdle(maxFreeConnect);
        config.setMinIdle(minFreeConnect);
        config.setMaxWaitMillis(maxWaitTime);
        config.setMaxTotal(maxConnect);
        JedisPool jedisPool = new JedisPool(config,host,port,timeOut,password);
        return jedisPool;
    }
}

接口

/**
 * @author oumiga
 */
public interface RedisLock{

    /**
     * 獲取鎖
     * @throws Exception
     */
    public void lock() throws Exception;

    /**
     * 釋放鎖
     * @throws Exception
     */
    public void closeLock() throws Exception;
}

實(shí)現(xiàn)類

import com.lpl.config.JedisConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.UUID;

/**
 * @author oumiga
 */
public class RedisLockImpl implements RedisLock {

    @Value("spring.jedis.redisKey")
    private String redisKey;

    @Value("spring.jedis.redisLockTimeOut")
    private int timeOut;

    @Autowired
    private JedisPool jedisPool;

    private String uuid = UUID.randomUUID().toString();

    @Override
    public void lock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        jedis.setnx(redisKey,uuid);
        jedis.expire(redisKey,timeOut);
    }

    @Override
    public void closeLock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        String value = jedis.get(redisKey);
         if(uuid.equals(value)){
             jedis.del(redisKey);
         }
    }
}

工具類

/**
 * @author oumiga
 */
public class RedisLockUtils {

    private static RedisLock redisLock = new RedisLockImpl();

    private static void lock() throws Exception{
        redisLock.lock();
    }

    private static void closeLock() throws Exception{
        redisLock.closeLock();
    }
}

這種實(shí)現(xiàn)方式還有一種弊端,比如說(shuō)當(dāng)實(shí)現(xiàn)類的 jedis.expire(redisKey,timeOut);執(zhí)行異常,但是該線程已經(jīng)獲取了鎖,沒(méi)有添加過(guò)期時(shí)間,如果程序一旦發(fā)生異常,就會(huì)導(dǎo)致死鎖現(xiàn)象。所以要保證事務(wù)的原子性。解決方法可以使用以下方法

import com.lpl.config.JedisConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.UUID;

/**
 * @author oumiga
 */
public class RedisLockImpl implements RedisLock {

    @Value("spring.jedis.redisKey")
    private String redisKey;

    @Value("spring.jedis.redisLockTimeOut")
    private int timeOut;

    @Autowired
    private JedisPool jedisPool;

    private String uuid = UUID.randomUUID().toString();

    @Override
    public void lock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        /*jedis.setnx(redisKey,uuid);
        jedis.expire(redisKey,timeOut);*/

        //使用該方法保證事務(wù)的原子性
        jedis.setex(redisKey,timeOut,uuid);
    }

    @Override
    public void closeLock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        String value = jedis.get(redisKey);
        if(uuid.equals(value)){
            jedis.del(redisKey);
        }
    }
}

當(dāng)大家讀到這里的時(shí)候其實(shí)還有一個(gè)問(wèn)題的存在,例如當(dāng)前線程還沒(méi)有執(zhí)行完畢,但是key的時(shí)間已經(jīng)過(guò)期,導(dǎo)致當(dāng)前線程獲取到的鎖被釋放。解決辦法就是在當(dāng)前線程中開啟一個(gè)子線程,并使用定時(shí)任務(wù)每隔一段實(shí)現(xiàn)重置一下當(dāng)前線程的過(guò)期時(shí)間。代碼如下

import com.lpl.config.JedisConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

/**
 * @author oumiga
 */
public class RedisLockImpl implements RedisLock {

    @Value("spring.jedis.redisKey")
    private String redisKey;

    @Value("spring.jedis.redisLockTimeOut")
    private int timeOut;

    @Autowired
    private JedisPool jedisPool;

    private String uuid = UUID.randomUUID().toString();

    private Thread thread;

    @Override
    public void lock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        /*jedis.setnx(redisKey,uuid);
        jedis.expire(redisKey,timeOut);*/

        //使用該方法保證事務(wù)的原子性
        jedis.setex(redisKey,timeOut,uuid);

        //創(chuàng)建一個(gè)子線程,來(lái)重置過(guò)期實(shí)現(xiàn),在正式開發(fā)中建議使用Executors或者ThreadPoolExecutor線程池創(chuàng)建線程
        thread = new Thread(()->{
            startTimeTask();
        });
        thread.start();
    }

    @Override
    public void closeLock() throws Exception{
        Jedis jedis = jedisPool.getResource();
        String value = jedis.get(redisKey);
        if(uuid.equals(value)){
            thread.interrupt();
            jedis.del(redisKey);
        }
    }

    /**
     * 定時(shí)任務(wù)
     */
    public void startTimeTask(){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Jedis jedis = jedisPool.getResource();
                jedis.expire(redisKey,timeOut);
            }
        },(timeOut - 1) * 1000);
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容