redis - 分布式锁

在java中,咱们能够用synchronized进行加锁,也能够用lock的lock方法加锁和unlock方法释放锁。可是在多个进程或者夸服务器的状况下,这种加锁的方式就没办法用了,由于他没办法让其余客户端的人知道是否被加锁了。因此咱们能够用redis、zookeeper、etcd等来实现。redis也有相似于lock的乐观锁,在redis - 商品交易中也展现了WATCH的使用,可是当key里的内容足够多时,监控频繁的变化反而致使性能的降低。
java的map,有putIfAbsent方法,意思是若是对应的key已经赋值了,则不能继续赋值。redis中,也有相似的方法,setnx,SET if Not eXists,也是若是对应的key有值了,则不能继续赋值,因此咱们能够用他这个方法做为分布式锁。java

// 锁的key
static String lockName = "lock:";
static int cnt = 1000;
static CountDownLatch countDownLatch = new CountDownLatch(cnt);

@Test
public void testLock() throws InterruptedException {
    JedisUtils.del(lockName);
    // 锁的时间,达到这个时间就释放
    int lockTime = 1;
    // 锁的超时时间,达到这个时间就放弃获取
    long timeOut = 2500;
    for (int i = 0; i < cnt; i++) {
        new Thread(new LockThread(i, lockTime, timeOut)).start();
        countDownLatch.countDown();
    }
    TimeUnit.SECONDS.sleep(3);
}

static class LockThread implements Runnable {
    int lockTime;
    long timeOut;
    int idx;

    public LockThread(int idx, int lockTime, long timeOut) {
        this.idx = idx;
        this.lockTime = lockTime;
        this.timeOut = timeOut;
    }

    @Override
    public void run() {
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String lock = lock(lockName, lockTime, timeOut);
        if (StringUtils.isNotEmpty(lock)) {
            System.out.println(idx + ":获取到了锁");
            //JedisUtils.del(lockName);
        }
    }
}

private static String lock(String lockName, int lockTime, long timeOut) {
    long end = System.currentTimeMillis() + timeOut;
    String value = UUID.randomUUID().toString();
    while (System.currentTimeMillis() < end) {
        long setnx = JedisUtils.setnx(lockName, value);
        // 1说明获取锁,返回
        if (setnx == 1) {
            JedisUtils.expire(lockName, lockTime);
            return value;
        }
        try {
            // 没获取则休眠100毫秒继续抢锁
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return null;
}

正常状况下,上面的分布式锁能够运行的,可是有如下几个问题:redis

  1. 设置了失效时间,若是在失效时间内没执行完,则其余进程就会获取到锁,致使同一时间有多个进程获取同一个锁。
  2. 若是在失效时间内获取到锁的进程已经崩溃,锁依然得不到释放,致使其余进程依然在等待失效过时。
  3. 若是只有一个redis服务器,那服务器崩溃则不能正常运行。若是redis作主从, A进程获取锁,且在主服务器数据未同步到从服务器的时候崩溃,B进程读取已经变成主服务器的原从服务器时,由于没有锁又获取到了锁,致使同一时间有多个进程获取同一个锁。
相关文章
相关标签/搜索