最近看到分布式锁这块,根据其他大神的说法,总结了几种基本实现方式
1、数据库乐观锁
2、redis锁
3、zookeeper
闲来无事,就写了一个相对实现比较简单的redis锁。不善于描述,直接贴代码吧!
1、maven带入相关jar包
redis.clients jedis 2.9.0
2、获取锁和释放锁的相关代码
public class RedisLock { private static JedisPool jedisPool; static { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(100); config.setMaxWaitMillis(3000); config.setTestOnBorrow(false); config.setMinIdle(10); jedisPool = new JedisPool(config, "127.0.0.1", 6379); } private static Jedis getClient() { return jedisPool.getResource(); } private static String key = "redis.lock"; private static String val = "true"; private static int expire = 60; //过期时间 //获取锁 public static boolean getLock() { for (; ; ) { Jedis jedis = null; try { jedis = getClient(); Long result = jedis.setnx(key, val); if (result == 1) { jedis.expire(key, expire); return true; } System.out.println(Thread.currentThread().getName() + "正在自旋"); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } } finally { jedisPool.returnResource(jedis); } } } //释放锁 public static void relaseLock() { getClient().expire(key, -1); System.out.println("释放锁成功"); } }
3、一个简单的数据记录中心,提供批量获取顺序号的方法
public class DataRange { private Long start; private Long end; public Long getStart() { return start; } public void setStart(Long start) { this.start = start; } public void setEnd(Long end) { this.end = end; } public Long getEnd() { return end; } @Override public String toString() { return "DataRange{" + "start=" + start + ", end=" + end + '}'; } }
public class DataCenter { private static Long num = 0L; private static Integer step = 500; public static DataRange getRangeNos() throws Exception { if (RedisLock.getLock()) { DataRange range = new DataRange(); range.setStart(num); num += step; range.setEnd(num - 1); // TimeUnit.SECONDS.sleep(10); RedisLock.relaseLock(); return range; } throw new Exception("获取流水号失败"); } }
4、一个补齐流水号和日期生成完整流水号的辅助类
public class DataUtil { private static final int length = 8; private static final String addc = "0"; private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); public static String complete(Long l) throws Exception { String dateStr = format.format(new Date()); String ls = l.toString(); int lsl = ls.length(); if (lsl < length) { StringBuilder sb = new StringBuilder(dateStr); for (int i = 0; i < length - lsl; i++) { sb.append(addc); } return sb.append(ls).toString(); } else if (lsl == length) { return dateStr + ls; } else { throw new Exception("out of range"); } } }
5、创建调用客户端,此处以线程表示
public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { try { DataRange range = DataCenter.getRangeNos(); System.out.println(this.getName() + "获取数组序列号" + range); } catch (Exception e) { e.printStackTrace(); } } } }
6、创建Test类
public class Test { public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new MyThread(); thread.start(); } } }
输出结果:
Thread-6正在自旋
Thread-2正在自旋
Thread-5正在自旋
Thread-0正在自旋
Thread-4正在自旋
Thread-8正在自旋
Thread-7正在自旋
释放锁成功
Thread-3获取数组序列号DataRange{start=0, end=499}
释放锁成功
Thread-3获取数组序列号DataRange{start=500, end=999}
释放锁成功
Thread-3获取数组序列号DataRange{start=1000, end=1499}
释放锁成功
Thread-3获取数组序列号DataRange{start=1500, end=1999}
释放锁成功
Thread-3获取数组序列号DataRange{start=2000, end=2499}
Thread-3正在自旋
释放锁成功
Thread-1获取数组序列号DataRange{start=2500, end=2999}
释放锁成功
Thread-1获取数组序列号DataRange{start=3000, end=3499}
释放锁成功
Thread-1获取数组序列号DataRange{start=3500, end=3999}
Thread-1正在自旋
释放锁成功
Thread-9获取数组序列号DataRange{start=4000, end=4499}
释放锁成功
Thread-9获取数组序列号DataRange{start=4500, end=4999}
释放锁成功
Thread-9获取数组序列号DataRange{start=5000, end=5499}
释放锁成功
Thread-9获取数组序列号DataRange{start=5500, end=5999}
释放锁成功
Thread-9获取数组序列号DataRange{start=6000, end=6499}
释放锁成功
Thread-9获取数组序列号DataRange{start=6500, end=6999}
释放锁成功
Thread-9获取数组序列号DataRange{start=7000, end=7499}
释放锁成功
Thread-9获取数组序列号DataRange{start=7500, end=7999}
释放锁成功
Thread-9获取数组序列号DataRange{start=8000, end=8499}
释放锁成功
Thread-9获取数组序列号DataRange{start=8500, end=8999}
Thread-2正在自旋
Thread-5正在自旋
释放锁成功
Thread-6获取数组序列号DataRange{start=9000, end=9499}
释放锁成功
Thread-6获取数组序列号DataRange{start=9500, end=9999}
释放锁成功
Thread-6获取数组序列号DataRange{start=10000, end=10499}
释放锁成功
Thread-6获取数组序列号DataRange{start=10500, end=10999}
Thread-4正在自旋
Thread-8正在自旋
Thread-6正在自旋
释放锁成功
Thread-0获取数组序列号DataRange{start=11000, end=11499}
释放锁成功
Thread-0获取数组序列号DataRange{start=11500, end=11999}
Thread-0正在自旋
释放锁成功
Thread-7获取数组序列号DataRange{start=12000, end=12499}
释放锁成功
Thread-7获取数组序列号DataRange{start=12500, end=12999}
释放锁成功
Thread-7获取数组序列号DataRange{start=13000, end=13499}
释放锁成功
Thread-7获取数组序列号DataRange{start=13500, end=13999}
释放锁成功
Thread-7获取数组序列号DataRange{start=14000, end=14499}
Thread-7正在自旋
释放锁成功
Thread-3获取数组序列号DataRange{start=14500, end=14999}
释放锁成功
Thread-3获取数组序列号DataRange{start=15000, end=15499}
释放锁成功
Thread-3获取数组序列号DataRange{start=15500, end=15999}
释放锁成功
Thread-3获取数组序列号DataRange{start=16000, end=16499}
释放锁成功
Thread-3获取数组序列号DataRange{start=16500, end=16999}
释放锁成功
Thread-1获取数组序列号DataRange{start=17000, end=17499}
释放锁成功
Thread-1获取数组序列号DataRange{start=17500, end=17999}
释放锁成功
Thread-1获取数组序列号DataRange{start=18000, end=18499}
释放锁成功
Thread-1获取数组序列号DataRange{start=18500, end=18999}
释放锁成功
Thread-1获取数组序列号DataRange{start=19000, end=19499}
释放锁成功
Thread-1获取数组序列号DataRange{start=19500, end=19999}
释放锁成功
Thread-1获取数组序列号DataRange{start=20000, end=20499}
释放锁成功
Thread-2获取数组序列号DataRange{start=20500, end=20999}
Thread-2正在自旋
释放锁成功
Thread-5获取数组序列号DataRange{start=21000, end=21499}
释放锁成功
Thread-5获取数组序列号DataRange{start=21500, end=21999}
释放锁成功
Thread-5获取数组序列号DataRange{start=22000, end=22499}
释放锁成功
Thread-5获取数组序列号DataRange{start=22500, end=22999}
释放锁成功
Thread-5获取数组序列号DataRange{start=23000, end=23499}
释放锁成功
Thread-5获取数组序列号DataRange{start=23500, end=23999}
Thread-8正在自旋
Thread-4正在自旋
释放锁成功
Thread-6获取数组序列号DataRange{start=24500, end=24999}
释放锁成功
Thread-5获取数组序列号DataRange{start=24000, end=24499}
Thread-5正在自旋
释放锁成功
Thread-6获取数组序列号DataRange{start=25000, end=25499}
释放锁成功
Thread-6获取数组序列号DataRange{start=25500, end=25999}
释放锁成功
Thread-6获取数组序列号DataRange{start=26000, end=26499}
释放锁成功
Thread-6获取数组序列号DataRange{start=26500, end=26999}
释放锁成功
Thread-6获取数组序列号DataRange{start=27000, end=27499}
释放锁成功
Thread-0获取数组序列号DataRange{start=27500, end=27999}
释放锁成功
Thread-0获取数组序列号DataRange{start=28000, end=28499}
释放锁成功
Thread-0获取数组序列号DataRange{start=28500, end=28999}
释放锁成功
Thread-0获取数组序列号DataRange{start=29000, end=29499}
释放锁成功
Thread-0获取数组序列号DataRange{start=29500, end=29999}
释放锁成功
Thread-0获取数组序列号DataRange{start=30000, end=30499}
释放锁成功
Thread-0获取数组序列号DataRange{start=30500, end=30999}
释放锁成功
Thread-0获取数组序列号DataRange{start=31000, end=31499}
释放锁成功
Thread-7获取数组序列号DataRange{start=31500, end=31999}
释放锁成功
Thread-7获取数组序列号DataRange{start=32000, end=32499}
释放锁成功
Thread-7获取数组序列号DataRange{start=32500, end=32999}
释放锁成功
Thread-7获取数组序列号DataRange{start=33000, end=33499}
释放锁成功
Thread-7获取数组序列号DataRange{start=33500, end=33999}
释放锁成功
Thread-2获取数组序列号DataRange{start=34000, end=34499}
释放锁成功
Thread-2获取数组序列号DataRange{start=34500, end=34999}
释放锁成功
Thread-2获取数组序列号DataRange{start=35000, end=35499}
释放锁成功
Thread-2获取数组序列号DataRange{start=35500, end=35999}
Thread-8正在自旋
释放锁成功
Thread-2获取数组序列号DataRange{start=36000, end=36499}
Thread-5正在自旋
Thread-2正在自旋
释放锁成功
Thread-4获取数组序列号DataRange{start=36500, end=36999}
释放锁成功
Thread-4获取数组序列号DataRange{start=37000, end=37499}
释放锁成功
Thread-4获取数组序列号DataRange{start=37500, end=37999}
释放锁成功
Thread-4获取数组序列号DataRange{start=38000, end=38499}
释放锁成功
Thread-4获取数组序列号DataRange{start=38500, end=38999}
释放锁成功
Thread-4获取数组序列号DataRange{start=39000, end=39499}
释放锁成功
Thread-4获取数组序列号DataRange{start=39500, end=39999}
释放锁成功
Thread-4获取数组序列号DataRange{start=40000, end=40499}
释放锁成功
Thread-4获取数组序列号DataRange{start=40500, end=40999}
释放锁成功
Thread-4获取数组序列号DataRange{start=41000, end=41499}
释放锁成功
Thread-8获取数组序列号DataRange{start=41500, end=41999}
释放锁成功
Thread-8获取数组序列号DataRange{start=42000, end=42499}
释放锁成功
Thread-8获取数组序列号DataRange{start=42500, end=42999}
释放锁成功
Thread-8获取数组序列号DataRange{start=43000, end=43499}
Thread-8正在自旋
释放锁成功
Thread-5获取数组序列号DataRange{start=43500, end=43999}
Thread-2正在自旋
释放锁成功
Thread-5获取数组序列号DataRange{start=44000, end=44499}
释放锁成功
Thread-5获取数组序列号DataRange{start=44500, end=44999}
Thread-2正在自旋
释放锁成功
Thread-8获取数组序列号DataRange{start=45000, end=45499}
释放锁成功
Thread-8获取数组序列号DataRange{start=45500, end=45999}
释放锁成功
Thread-8获取数组序列号DataRange{start=46000, end=46499}
释放锁成功
Thread-8获取数组序列号DataRange{start=46500, end=46999}
释放锁成功
Thread-8获取数组序列号DataRange{start=47000, end=47499}
释放锁成功
Thread-8获取数组序列号DataRange{start=47500, end=47999}
释放锁成功
Thread-2获取数组序列号DataRange{start=48000, end=48499}
释放锁成功
Thread-2获取数组序列号DataRange{start=48500, end=48999}
释放锁成功
Thread-2获取数组序列号DataRange{start=49000, end=49499}
释放锁成功
Thread-2获取数组序列号DataRange{start=49500, end=49999}
总结:使用redis的setnx来控制获取哪个客户端获取锁(目前上述获取锁方法存在同步的问题,设置keyvalue和设置过期时间不同步会导致某些情况死锁),,获取的流水号每次获取多个,可供一段时间使用。由于本机测试,仅供学习参考,需要的可以自己修改完善代码。
此处datacenter可以改为数据库或者redis缓存存储
或者直接使用数据库乐观锁实现方式也可以。