redis分布式锁原理以及实现

 

概念:

线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。

进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。

分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。

 

Redis实现分布式锁的原理:

1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0

2.死锁问题:通过实践来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,

相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程, 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍

(坑)针对集群服务器时间不一致问题,可以调用redis的time()获取当前时间

 

流程图:

代码:

/**
 * @author yaoxin
 * @date 2018/8/13下午5:04
 */
public class RedisLockTest {

    public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8";
    public static final String name = "com.mysql.jdbc.Driver";
    public static final String user = "root";
    public static final String password = "";

    public static void main(String[] args) {

        Integer count = 50;
        while (count > 0) {
            count--;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Jedis jedis = new Jedis("127.0.0.1", 6379);
                    jedis.auth("1234");
                    String lock = lock(jedis);
                    if (lock != null) {
                        Statement statement = null;
                        Connection conn = null;
                        ResultSet resultSet = null;
                        try {
                            Class.forName(name);// 指定连接类型
                            conn = DriverManager.getConnection(url, user, password);// 获取连接
                            statement = conn.createStatement();// 准备执行语句
                            String querySql = "SELECT id,name,count FROM production WHERE id=2";
                            resultSet = statement.executeQuery(querySql);
                            int count = 0;
                            while (resultSet.next()) {
                                System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id")
                                        + " name: " + resultSet.getString("name")
                                        + " count: " + resultSet.getString("count"));
                                count = Integer.valueOf(resultSet.getString("count"));
                            }
                            String updateSql = "UPDATE production SET count=" + (count - 1)
                                    + " WHERE id=2";
                            int rows = statement.executeUpdate(updateSql);
                            if (rows > 0) {
                                System.out.println("更新成功" + Thread.currentThread().getName() + "  库存剩余:" + (count - 1));
                                System.out.println(Thread.currentThread().getName() + " === > >开始解锁");
                                boolean unlock = unlock(jedis, lock);
                                if (unlock)
                                    System.out.println(Thread.currentThread().getName() + " === > >解锁成功");
                            } else {
                                System.out.println("更新失败" + Thread.currentThread().getName());
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            try {
                                if (conn != null)
                                    conn.close();
                                if (statement != null)
                                    statement.close();
                                if (resultSet != null)
                                    resultSet.close();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }, "线程" + count).start();
        }
    }

    public static String lock(Jedis jedis) {
        try {
            while (true) {
                String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + "";
                if (jedis.setnx("lock", lockTime) == 1) {
                    jedis.expire("lock", 5);
                    return lockTime;
                }
                String lock = jedis.get("lock");
                if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) {
                    String oldLockTime = jedis.getSet("lock", lockTime);
                    if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) {
                        return lockTime;
                    }
                }
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static boolean unlock(Jedis jedis, String lockTag) {
        if (lockTag.equals(jedis.get("lock"))) {
            jedis.del("lock");
            return true;
        }
        return false;
    }

}

 

运行结果如下图:

 

珍惜身边的人

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值