场景介绍html
不少互联网场景(如商品秒杀,论坛回帖盖楼等),须要用加锁的方式,以对某种资源进行顺序访问控制。若是应用服务集群部署,则涉及到对分布式应用加锁。当前分布式加锁主要有三种方式:(磁盘)数据库、缓存数据库、Zookeeper。接下里让咱们一块儿看看加锁实践过程。java
加锁实现redis
1 package dcsDemo01; 2 3 import java.util.UUID; 4 5 import redis.clients.jedis.Jedis; 6 7 public class DistributedLock { 8 private final String host = "192.168.0.220"; 9 private final int port = 6379; 10 11 private static final String SUCCESS = "OK"; 12 private static final String SET_IF_NOT_EXIST = "NX"; 13 private static final String EXPIRE_TIME = "PX"; 14 15 public DistributedLock(){} 16 17 /* 18 * @param lockName 锁名 19 * @param timeout 获取锁的超时时间 20 * @param lockTimeout 锁的有效时间 21 * @return 锁的标识 22 */ 23 public String getLockWithTimeout(String lockName, long timeout, long lockTimeout) { 24 String ret = null; 25 Jedis jedisClient = new Jedis(host, port); 26 27 try { 28 String authMsg = jedisClient.auth("Demo@123"); 29 if (!SUCCESS.equals(authMsg)) { 30 System.out.println("AUTH FAILED: " + authMsg); 31 } 32 33 String identifier = UUID.randomUUID().toString(); 34 String lockKey = "DLock:" + lockName; 35 long end = System.currentTimeMillis() + timeout; 36 37 while(System.currentTimeMillis() < end) { 38 String result = jedisClient.set(lockKey, identifier, SET_IF_NOT_EXIST, EXPIRE_TIME, lockTimeout); 39 if(SUCCESS.equals(result)) { 40 ret = identifier; 41 break; 42 } 43 44 try { 45 Thread.sleep(2); 46 } catch (InterruptedException e) { 47 Thread.currentThread().interrupt(); 48 } 49 } 50 } 51 catch (Exception e) { 52 e.printStackTrace(); 53 }finally { 54 jedisClient.quit(); 55 jedisClient.close(); 56 } 57 58 return ret; 59 } 60 61 /* 62 * @param lockName 锁名 63 * @param identifier 锁的标识 64 */ 65 public void releaseLock(String lockName, String identifier) { 66 Jedis jedisClient = new Jedis(host, port); 67 68 try { 69 String authMsg = jedisClient.auth("Demo@123"); 70 if (!SUCCESS.equals(authMsg)) { 71 System.out.println("AUTH FAILED: " + authMsg); 72 } 73 74 String lockKey = "DLock:" + lockName; 75 if(identifier.equals(jedisClient.get(lockKey))) { 76 jedisClient.del(lockKey); 77 } 78 } 79 catch (Exception e) { 80 e.printStackTrace(); 81 }finally { 82 jedisClient.quit(); 83 jedisClient.close(); 84 } 85 } 86 }
测试代码数据库
假设20个线程对10台mate10手机进行抢购:缓存
package dcsDemo01; import java.util.UUID; public class CaseTest { public static void main(String[] args) { ServiceOrder service = new ServiceOrder(); for (int i = 0; i < 20; i++) { ThreadBuy client = new ThreadBuy(service); client.start(); } } } class ServiceOrder { private final int MAX = 10; DistributedLock DLock = new DistributedLock(); int n = 10; public void handleOder() { String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName(); String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000); System.out.println("正在为用户:" + userName + " 处理订单"); if(n > 0) { int num = MAX - n + 1; System.out.println("用户:"+ userName + "购买第" + num + "台,剩余" + (--n) + "台"); }else { System.out.println("用户:"+ userName + "没法购买,已售罄!"); } DLock.releaseLock("Huawei Mate 10", identifier); } } class ThreadBuy extends Thread { private ServiceOrder service; public ThreadBuy(ServiceOrder service) { this.service = service; } @Override public void run() { service.handleOder(); } }
运行结果dom
配置好实际的缓存实例链接地址、端口与链接密码,运行代码,获得如下结果:分布式
正在为用户:eee56fb7Thread-16 处理订单 用户:eee56fb7Thread-16购买第1台,剩余9台 正在为用户:d6521816Thread-2 处理订单 用户:d6521816Thread-2购买第2台,剩余8台 正在为用户:d7b3b983Thread-19 处理订单 用户:d7b3b983Thread-19购买第3台,剩余7台 正在为用户:36a6b97aThread-15 处理订单 用户:36a6b97aThread-15购买第4台,剩余6台 正在为用户:9a973456Thread-1 处理订单 用户:9a973456Thread-1购买第5台,剩余5台 正在为用户:03f1de9aThread-14 处理订单 用户:03f1de9aThread-14购买第6台,剩余4台 正在为用户:2c315ee6Thread-11 处理订单 用户:2c315ee6Thread-11购买第7台,剩余3台 正在为用户:2b03b7c0Thread-12 处理订单 用户:2b03b7c0Thread-12购买第8台,剩余2台 正在为用户:75f25749Thread-0 处理订单 用户:75f25749Thread-0购买第9台,剩余1台 正在为用户:26c71db5Thread-18 处理订单 用户:26c71db5Thread-18购买第10台,剩余0台 正在为用户:c32654dbThread-17 处理订单 用户:c32654dbThread-17没法购买,已售罄! 正在为用户:df94370aThread-7 处理订单 用户:df94370aThread-7没法购买,已售罄! 正在为用户:0af94cddThread-5 处理订单 用户:0af94cddThread-5没法购买,已售罄! 正在为用户:e52428a4Thread-13 处理订单 用户:e52428a4Thread-13没法购买,已售罄! 正在为用户:46f91208Thread-10 处理订单 用户:46f91208Thread-10没法购买,已售罄! 正在为用户:e0ca87bbThread-9 处理订单 用户:e0ca87bbThread-9没法购买,已售罄! 正在为用户:f385af9aThread-8 处理订单 用户:f385af9aThread-8没法购买,已售罄! 正在为用户:46c5f498Thread-6 处理订单 用户:46c5f498Thread-6没法购买,已售罄! 正在为用户:935e0f50Thread-3 处理订单 用户:935e0f50Thread-3没法购买,已售罄! 正在为用户:d3eaae29Thread-4 处理订单 用户:d3eaae29Thread-4没法购买,已售罄!
//测试类中注释两行用于加锁的代码: public void handleOder() { String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName(); //加锁代码 //String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000); System.out.println("正在为用户:" + userName + " 处理订单"); if(n > 0) { int num = MAX - n + 1; System.out.println("用户:"+ userName + "够买第" + num + "台,剩余" + (--n) + "台"); }else { System.out.println("用户:"+ userName + "没法够买,已售罄!"); } //加锁代码 //DLock.releaseLock("Huawei Mate 10", identifier); } 注释加锁代码后的运行结果,能够看出处理过程是无序的: 正在为用户:e04934ddThread-5 处理订单 正在为用户:a4554180Thread-0 处理订单 用户:a4554180Thread-0购买第2台,剩余8台 正在为用户:b58eb811Thread-10 处理订单 用户:b58eb811Thread-10购买第3台,剩余7台 正在为用户:e8391c0eThread-19 处理订单 正在为用户:21fd133aThread-13 处理订单 正在为用户:1dd04ff4Thread-6 处理订单 用户:1dd04ff4Thread-6购买第6台,剩余4台 正在为用户:e5977112Thread-3 处理订单 正在为用户:4d7a8a2bThread-4 处理订单 用户:e5977112Thread-3购买第7台,剩余3台 正在为用户:18967410Thread-15 处理订单 用户:18967410Thread-15购买第9台,剩余1台 正在为用户:e4f51568Thread-14 处理订单 用户:21fd133aThread-13购买第5台,剩余5台 用户:e8391c0eThread-19购买第4台,剩余6台 正在为用户:d895d3f1Thread-12 处理订单 用户:d895d3f1Thread-12没法购买,已售罄! 正在为用户:7b8d2526Thread-11 处理订单 用户:7b8d2526Thread-11没法购买,已售罄! 正在为用户:d7ca1779Thread-8 处理订单 用户:d7ca1779Thread-8没法购买,已售罄! 正在为用户:74fca0ecThread-1 处理订单 用户:74fca0ecThread-1没法购买,已售罄! 用户:e04934ddThread-5购买第1台,剩余9台 用户:e4f51568Thread-14购买第10台,剩余0台 正在为用户:aae76a83Thread-7 处理订单 用户:aae76a83Thread-7没法购买,已售罄! 正在为用户:c638d2cfThread-2 处理订单 用户:c638d2cfThread-2没法购买,已售罄! 正在为用户:2de29a4eThread-17 处理订单 用户:2de29a4eThread-17没法购买,已售罄! 正在为用户:40a46ba0Thread-18 处理订单 用户:40a46ba0Thread-18没法购买,已售罄! 正在为用户:211fd9c7Thread-9 处理订单 用户:211fd9c7Thread-9没法购买,已售罄! 正在为用户:911b83fcThread-16 处理订单 用户:911b83fcThread-16没法购买,已售罄! 用户:4d7a8a2bThread-4购买第8台,剩余2台
总的来讲,使用DCS服务中Redis类型的缓存实例实现分布式加锁,有几大优点:ide
一、加锁操做简单,使用SET、GET、DEL等几条简单命令便可实现锁的获取和释放。性能
二、性能优越,缓存数据的读写优于磁盘数据库与Zookeeper。测试
三、可靠性强,DCS有主备和集群实例类型,避免单点故障。
以上代码实现仅展现使用DCS服务进行加锁访问的便捷性,具体技术实现须要考虑死锁、锁的检查等状况,欢迎点击分布式缓存服务DCS了解更多。