引入:
一.为什么要使用分布式锁,用同步关键字synchronized,或者lock锁不能实现吗?
答:同步关键字与lock锁是基于jvm系统级别的,只能适用于同一jvm;对于微服务架构的各个单独部署的服务来说,各个微服务之间不同jvm,所以这时候只能依靠分布式锁来实现同步。
二.分布式锁的必须满足的特性?
1互斥性:锁的最基本特性;2可重入:获取锁后可以再次获取,常见于递归调用的场景;3锁超时:因为获取锁与释放锁并不是原操作,设置时间可以避免死锁;
三.分布式锁的实现又哪些?
1数据库层面的乐观锁;2基于zookeeper的节点的实现;3基于redis中间件的实现
性能比较:redis中间件>zookeeper=数据库
可靠性比较:zookeeper>redis中间件>数据库
四.介绍redisson:
redisson是redis的一个组件,是redis提供的一种分布锁的实现,它实现的jdk lock锁的接口,还有就是其底层是lua脚本,将锁的获取与锁的设置超时时间实现原子性操作,避免获取到锁而服务器宕机超时时间设置失败的情况。
源码如下,重点代码用红色颜色标注,其中又lua脚本代码
@Override
public boolean tryLock() {
return get(tryLockAsync());
}@Override
public RFuture<Boolean> tryLockAsync() {
return tryLockAsync(Thread.currentThread().getId());
}@Override
public RFuture<Boolean> tryLockAsync(long threadId) {
return tryAcquireOnceAsync(-1, null, threadId);
}private RFuture<Boolean> tryAcquireOnceAsync(long leaseTime, TimeUnit unit, final long threadId) {
if (leaseTime != -1) {
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
}
RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
ttlRemainingFuture.addListener(new FutureListener<Boolean>() {
@Override
public void operationComplete(Future<Boolean> future) throws Exception {
if (!future.isSuccess()) {
return;
}Boolean ttlRemaining = future.getNow();
// lock acquired
if (ttlRemaining) {
scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
}<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);//lua脚本
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}
————————————————————————————————————————————————————————
redisson的demo
1.引入redisson的依赖
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.9.1</version> </dependency>
2.redisson实现代码
public class SellTicket_Redisson implements Runnable { // 100张票 private int tickets = 100; // Redisson 实现 RLock lock = getRedissonLock(); private RLock getRedissonLock() { //获取redisson锁实例 Config config = new Config(); config.useSingleServer().setAddress("redis://localhost:6379"); Redisson redisson = (Redisson) Redisson.create(config); // 获得锁对象实例 RLock lock = redisson.getLock("lock_Key"); return lock; } @Override public void run() { while (tickets >0) { //开启锁 lock.lock(1, TimeUnit.MINUTES); // 获取锁,并设置超时时间 try { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第 " + tickets-- + " 张票"); } } finally { lock.unlock(); // 释放锁 } } } }
3.test代码
public class SellTicketTest { public static void main(String[] args) { SellTicket_Redisson st = new SellTicket_Redisson(); // 三个线程模拟三个窗口售票 for (int i=1; i<=3; i++) { new Thread(st, "窗口" + i).start(); } } }