在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