這篇文章帶給大家的內容是關於利用Redis實現分散式鎖的詳解(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
選用Redis實現分散式鎖定原因
Redis有很高的效能
Redis指令對此支援較好,實作起來比較方便
使用指令介紹
SETNX
SETNX key valexpire
expire key timeoutdelete
delete key在使用Redis實現分散式鎖定的時候,主要就會使用到這三個指令。
實作
使用的是jedis來連接Redis。
實作想法
取得鎖的時候,使用setnx加鎖,並使用expire指令為鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值為一個隨機產生的UUID,透過此在釋放鎖的時候進行判斷。
取得鎖定的時候也設定一個取得的逾時時間,若超過這個時間就放棄取得鎖定。
釋放鎖的時候,透過UUID判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。
分散式鎖定的核心程式碼如下:
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import redis.clients.jedis.exceptions.JedisException; import java.util.List; import java.util.UUID; public class DistributedLock { private final JedisPool jedisPool; public DistributedLock(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 加锁 * @param locaName 锁的key * @param acquireTimeout 获取超时时间 * @param timeout 锁的超时时间 * @return 锁标识 */ public String lockWithTimeout(String locaName, long acquireTimeout, long timeout) { Jedis conn = null; String retIdentifier = null; try { // 获取连接 conn = jedisPool.getResource(); // 随机生成一个value String identifier = UUID.randomUUID().toString(); // 锁名,即key值 String lockKey = "lock:" + locaName; // 超时时间,上锁后超过此时间则自动释放锁 int lockExpire = (int)(timeout / 1000); // 获取锁的超时时间,超过这个时间则放弃获取锁 long end = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() results = transaction.exec(); if (results == null) { continue; } retFlag = true; } conn.unwatch(); break; } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retFlag; } }
測試
下面就用一個簡單的範例測試剛才實作的分散式鎖定。
例子中使用50個執行緒模擬秒殺一個商品,使用--運算子來實現商品減少,從結果有序性就可以看出是否為加鎖狀態。
模擬秒殺服務,在其中配置了jedis執行緒池,在初始化的時候傳給分散式鎖,供其使用。
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class Service { private static JedisPool pool = null; static { JedisPoolConfig config = new JedisPoolConfig(); // 设置最大连接数 config.setMaxTotal(200); // 设置最大空闲数 config.setMaxIdle(8); // 设置最大等待时间 config.setMaxWaitMillis(1000 * 100); // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 config.setTestOnBorrow(true); pool = new JedisPool(config, "127.0.0.1", 6379, 3000); } DistributedLock lock = new DistributedLock(pool); int n = 500; public void seckill() { // 返回锁的value值,供释放锁时候进行判断 String indentifier = lock.lockWithTimeout("resource", 5000, 1000); System.out.println(Thread.currentThread().getName() + "获得了锁"); System.out.println(--n); lock.releaseLock("resource", indentifier); } }
模擬執行緒進行秒殺服務
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.seckill(); } } public class Test { public static void main(String[] args) { Service service = new Service(); for (int i = 0; i <p>結果如下,結果為有序的。 </p><p style="text-align: center;"><span class="img-wrap"><img src="https://img.php.cn//upload/image/211/984/157/1540025904941327.png" title="1540025904941327.png" alt="利用Redis實現分散式鎖的詳解(程式碼範例)"></span></p><p>若註解掉使用鎖定的部分</p><pre class="brush:php;toolbar:false">public void seckill() { // 返回锁的value值,供释放锁时候进行判断 //String indentifier = lock.lockWithTimeout("resource", 5000, 1000); System.out.println(Thread.currentThread().getName() + "获得了锁"); System.out.println(--n); //lock.releaseLock("resource", indentifier); }
從結果可以看出,有些是非同步進行的。
在分散式環境中,對資源上鎖有時候是很重要的,例如搶購某一資源,這時候使用分散式鎖就可以很好地控制資源。
當然,在具體使用中,還需要考慮許多因素,例如逾時時間的選取,而取得鎖定時間的選取對並發量都有很大的影響,上述實現的分散式鎖定也只是一種簡單的實現,主要是一種思想。
[其實,使用zookeeper的可靠性是要大於使用redis實現的分散式鎖定的,但是相比而言,redis的效能更好。 ]
以上是利用Redis實現分散式鎖的詳解(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!