面试官 名目中如何成功散布式锁
散布式锁(Distributed Lock)是一种用于散布式系统中的同步机制,关键是为了防止散布式系统中,多个服务虚例同时操作一个共享资源所带来的并发安保疑问。
散布式锁的成功打算有多种,例如以下这几种:
综合以上面案来看,基于数据库成功的散布式锁不实用于高并发场景,而基于 Zookeeper 成功的散布式锁又须要额外部署 Zookeeper 服务,参与了运营老本,所以经常使用 Redis 成功散布式锁是目前干流的成功打算。
2.为什么Redis可以成功散布式锁?
由于Redis 作为一个独立的第三方系统(数据两边件),其自身就支持散布式运行。也就是针关于 Redis 的一切操作,一切的散布式系统都是全局可见的,如下图所示:
经常使用 Redis 成功散布式锁的打算有以下 4 种:
疑问解释
综合以上成功打算来看,消费级别经常使用 Redis 成功散布式锁的打算,应该决定 Redisson 框架。
Redisson 是一个开源的用于操作 Redis 的 Java 框架。与 Jedis 和 Lettuce 等轻量级的 Redis 框架不同,它提供了更初级且性能丰盛的 Redis 客户端。它提供了许多简化 Redis 操作的初级 API,并支持散布式对象、散布式锁、散布式汇合等个性。
参与 Redisson 依赖:
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId></dependency>
将 RedissonClient 对象保留到 Spring Ioc 容器,并为其设置 Redis 服务衔接信息,详细实现代码如下:
import org.redisson.Redisson;import org.redisson.api.RedissonClient;import org.redisson.config.Config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class RedissonConfig {@Beanpublic RedissonClient redissonClient() {Config config = new Config();// 也可以将 redis 性能信息保留到性能文件config.useSingleServer().setAddress("redis://127.0.0.1:6379");return Redisson.create(config);}}
Redisson 散布式锁的操作和 Java 中的 ReentrantLock(可重入锁)的操作很像,都是先经常使用 tryLock 尝试失掉(非偏心)锁,再经过 unlock 监禁锁,详细成功如下:
import org.redisson.api.RLock;import org.redisson.api.RedissonClient;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;@RestControllerpublic class LockController {@Autowiredprivate RedissonClient redissonClient;@GetMapping("/lock")public String lockResource() throws InterruptedException {String lockKey = "myLock";// 失掉 RLock 对象RLock lock = redissonClient.getLock(lockKey);try {// 尝试失掉锁(尝试加锁)(锁超时时期是 30 秒)boolean isLocked = lock.tryLock(30, TimeUnit.SECONDS);if (isLocked) {// 成功失掉到锁try {// 模拟业务处置TimeUnit.SECONDS.sleep(5);return "成功失掉锁,并口头业务代码";} catch (InterruptedException e) {e.printStackTrace();} finally {// 监禁锁lock.unlock();}} else {// 失掉锁失败return "失掉锁失败";}} catch (InterruptedException e) {e.printStackTrace();}return "失掉锁成功";}}
Redisson 自动创立的散布式锁是非偏心锁(出于性能的思考),想要把它变成偏心锁可经常使用以下代码成功:
RLock lock = redissonClient.getFairLock(lockKey);// 失掉偏心锁
Redisson 还可以创立读写锁,如下代码所示:
RReadWriteLock lock = redissonClient.getReadWriteLock(lockKey); // 失掉读写锁lock.readLock();// 读锁lock.writeLock(); // 写锁
读写锁的特点就是并发性能高,它是准许多个线程同时失掉读锁启动读操作的,也就是说在没有写锁的状况下,读取操作可以并发口头,提高了系统的并行度。但写锁则是独占式的,同一时期只要一个线程可以取得写锁,无论是读还是写都无法与写锁并存,这样就确保了数据修正时的数据分歧性。
Redisson 也支持联锁,也叫散布式多锁 MultiLock,它准许客户端一次性性失掉多个独立资源(RLock)上的锁,这些资源或者是不同的键或同一键的不同锁。当一切指定的锁都被成功失掉后,才会以为整个操作成功锁定。这样能够确保在散布式环境下启动跨资源的并发控制。
联锁的成功示例如下:
// 失掉须要加锁的资源RLock lock1 = redisson.getLock("lock1");RLock lock2 = redisson.getLock("lock2");// 联锁RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2);try {// 一次性性尝试失掉一切锁if (multiLock.tryLock()) {// 失掉锁成功...}} finally {// 监禁一切锁multiLock.unlock();}