基于redis分布式锁

错误方法:
setnx获取锁,拿到锁用expire给锁加一个过期时间,防止锁忘记释放。如果setnx执行之后expire执行之前,线程死掉,那锁就永远得不到释放,发生死锁。
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// 线程死掉,无法设置过期时间,发生死锁
jedis.expire(lockKey, expireTime);
}
 
最佳实践:set=setnx和expire,一条指令,把setnx和expire原子化结合起来。
 
set key value [ex seconds] [px milliseconds] [nx|xx]
ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功, 用于添加。

xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。

 

redis连接获取工具类

package com.hong.api.redis.lock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * 单机模式
 */
public class RedisUtil {
    private static final String redisHost = "192.168.1.200";
    private static final int port = 6379;
    private static JedisPoolConfig config = null;
    private static JedisPool pool = null;

    static {
        // 利用Redis连接池,保证多个线程利用多个连接,充分模拟并发性
        config = new JedisPoolConfig();
        config.setMaxIdle(10);
        config.setMaxTotal(30);
        config.setMaxWaitMillis(100000);

//        config.setTestOnBorrow(true);
//        config.setTestOnReturn(true);

        pool = new JedisPool(config, redisHost, port);
    }

    public static Jedis getJedis() {
        return pool.getResource();
    }

    public static int getNumActive() {
        return pool.getNumActive();
    }

    public static void returnResource(Jedis jedis) {
//        pool.returnBrokenResource(jedis);
        pool.returnResource(jedis);
    }
}

获取分布式锁

package com.hong.api.redis.lock;

import java.util.Random;
import java.util.UUID;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class RedisLock {

    /**
     * 获取锁:生成一个UUID,作为Key的标识,不断轮询lockName,直到set成功,表示成功获取锁。
     */
    public static TmpResult getLock(String lockNameKey) {
        String value = UUID.randomUUID().toString();
        Jedis jedis = null;
        for (; ; ) {
            try {
                jedis = RedisUtil.getJedis();
                if ("OK".equals(jedis.set(lockNameKey, value, "NX", "EX", 10))) {
                    System.out.println("get lock");

                    RedisUtil.returnResource(jedis);

                    TmpResult tmpResult = new TmpResult();
                    tmpResult.setJedis(jedis);
                    tmpResult.setValue(value);
                    return tmpResult;
                }

                // 阻塞之前,先放入redis pool,避免消费者线程太多把redis pool全部占满
                RedisUtil.returnResource(jedis);

    // 时间短,竞争比较激烈,性能差
//                Thread.currentThread().sleep(100);
                Thread.currentThread().sleep(new Random().nextInt(5000));

//                System.out.println("get lock fail");
} catch (Throwable t) {
        t.printStackTrace();

        if (jedis != null) {
        RedisUtil.returnResource(jedis);
        }
        }
        }
        }

    /**
     * 释放锁:对lockName做watch,开启一个事务,删除以LockName为key的锁,删除后此锁对于其他线程为可争抢。
     */
    public static void relaseLock(String lockNameKey, TmpResult tmpResult) {
        Jedis jedis = null;
        try {
            jedis = tmpResult.getJedis();
            while (true) {
                jedis.watch(lockNameKey);
                if (jedis.get(lockNameKey).equals(tmpResult.getValue())) {
                    Transaction tx = jedis.multi();
                    tx.del(lockNameKey);
                    tx.exec();
                    return;
                }
                jedis.unwatch();
            }
        } finally {
            if (jedis != null) {
                RedisUtil.returnResource(jedis);
            }
        }
    }
}

 

package com.hong.api.redis.lock;

import redis.clients.jedis.Jedis;

public class TmpResult {
    private Jedis jedis;
    private String value;

    public Jedis getJedis() {
        return jedis;
    }

    public void setJedis(Jedis jedis) {
        this.jedis = jedis;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

测试

package com.hong.api.redis.lock;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class RedisLockTest {
    private static String lockNameKey = "lock_key";

    private static int count = 0;
    private static AtomicInteger Countor = new AtomicInteger(0);

    private static int ThLeng = 1024;
    // CountDownLatch保证主线程在全部线程结束之后退出
    private static CountDownLatch latch = new CountDownLatch(ThLeng);

    private static ExecutorService service = Executors.newFixedThreadPool(ThLeng);

    public static void main(String args[]) {
        for (int i = 0; i < ThLeng; ++i) {
            String threadName = "thread-" + i;
            Thread t = new Thread(new SubAddThread(threadName));
            System.out.println(threadName + " init...");
            service.submit(t);
        }
        service.shutdown();
        try {
            latch.await();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        System.out.println(Countor.get());
        System.out.println(count);
    }

    public static class SubAddThread implements Runnable {
        private String threadName;

        public SubAddThread(String threadName) {
            this.threadName = threadName;
        }

        @Override
        public void run() {
            System.out.println(threadName + " starting...");
            for (int i = 0; i < 50; ++i) {
                TmpResult tmpResult = RedisLock.getLock(lockNameKey);
//                System.out.println(threadName + " get Lock");
                count++;
                Countor.incrementAndGet();
                RedisLock.relaseLock(lockNameKey, tmpResult);
                System.out.println(threadName + " " + count);
            }
            latch.countDown();
            System.out.println(threadName + " over");
        }
    }
}

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值