在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