>  기사  >  Java  >  Java 분산 잠금을 구현하는 방법

Java 분산 잠금을 구현하는 방법

王林
王林앞으로
2023-04-25 18:19:071727검색

1. 분산 잠금 소개

단일 머신 멀티스레딩: Java에서는 일반적으로 ReetrantLock 클래스 및 동기화 키워드와 같은 로컬 잠금을 사용하여 JVM 프로세스의 여러 스레드가 로컬 공유 리소스에 액세스하는 것을 제어합니다

Java 분산 잠금을 구현하는 방법

분산 시스템: 다양한 서비스/클라이언트가 일반적으로 독립적인 JVM 프로세스에서 실행됩니다. 여러 JVM 프로세스가 동일한 리소스를 공유하는 경우 로컬 잠금을 사용하여 리소스에 상호 배타적으로 액세스할 수 있는 방법이 없습니다. 따라서 분산 잠금이 탄생했습니다.

예: 총 3개의 시스템 주문 서비스 사본이 배포되었으며, 모두 외부 세계에 서비스를 제공합니다. 사용자는 주문을 하기 전에 재고를 확인해야 합니다. 과잉 판매를 방지하려면 재고 확인 작업에 대한 동기 액세스를 달성하기 위한 잠금이 필요합니다. 주문 서비스가 다른 JVM 프로세스에 있으므로 이 경우 로컬 잠금이 제대로 작동하지 않습니다. 여러 스레드가 동일한 JVM 프로세스에 있지 않더라도 동일한 잠금을 획득하여 공유 리소스에 대한 상호 배타적 액세스를 달성할 수 있도록 분산 잠금을 사용해야 합니다.

Java 분산 잠금을 구현하는 방법

가장 기본적인 분산 잠금 요구 사항:

  • 상호 배제: 언제든지 하나의 스레드에서만 잠금을 유지할 수 있습니다.

  • 고가용성: 잠금 서비스의 가용성이 높습니다. 또한 잠금을 해제하기 위한 클라이언트의 코드 논리에 문제가 있더라도 잠금은 결국 해제되며 공유 리소스에 대한 다른 스레드의 액세스에는 영향을 미치지 않습니다.

  • 재진입자: 노드가 잠금을 획득한 후 다시 잠금을 획득할 수 있습니다.

2. Redis 기반 분산 잠금 구현

1. Redis 기반으로 가장 간단한 분산 잠금 구현 방법

로컬 잠금이든 분산 잠금이든 핵심은 == "상호 배제"에 있습니다 = =.

Redis에서는 SETNX 命令是可以帮助我们实现互斥。SETNX가 존재하지 않는 경우 SET입니다(Java의 setIfAbsent 메서드에 해당). 키가 없으면 키 값이 설정됩니다. 키가 이미 존재하는 경우 SETNX는 아무 작업도 수행하지 않습니다.

> SETNX lockKey UniqueValue
(integer) 1
> SETNX lockKey UniqueValue
(integer) 0

잠금을 해제하려면 DEL 명령

> 정수) 1

실수로 다른 잠금을 삭제하는 것을 방지하기 위해 여기서는 Lua 스크립트를 사용하여 키에 해당하는 값(고유 값)으로 판단하는 것이 좋습니다.

Lua 스크립트는 잠금 해제 작업의 원자성을 보장하기 위해 선택되었습니다. Redis는 Lua 스크립트를 원자적 방식으로 실행할 수 있으므로 잠금 해제 작업의 원자성을 보장합니다.

// 释放锁时,先比较锁对应的 value 值是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Java 분산 잠금을 구현하는 방법

이것은 가장 간단한 Redis 분산 잠금 구현입니다. 구현 방법이 비교적 간단하고 성능이 매우 효율적입니다. 그러나 이러한 방식으로 분산 잠금을 구현하는 데에는 몇 가지 문제가 있습니다. 예를 들어, 잠금 해제 논리가 갑자기 중단되는 등 애플리케이션에 몇 가지 문제가 발생하면 잠금이 해제되지 않을 수 있으며 다른 스레드/프로세스에서 더 이상 공유 리소스에 액세스할 수 없습니다.

2. 잠금에 만료 시간을 설정하는 이유

주로 잠금이 해제되는 것을 방지하기 위한 것입니다

127.0.0.1:6379> SET lockKey UniqueValue EX 3 NX

OK

  • lockKey: 잠긴 잠금 이름 ;

  • uniqueValue: 잠금을 고유하게 식별할 수 있는 임의의 문자열

  • NX: SET은 lockKey에 해당하는 키 값이 존재하지 않는 경우에만 성공할 수 있습니다.

  • EX: 만료 시간 설정( (초) ) EX 3은 이 잠금의 자동 만료 시간이 3초임을 나타냅니다. EX에 해당하는 것은 PX(밀리초)이며 둘 다 만료 시간 설정입니다.

지정된 키의 값과 만료 시간 설정이 원자성 작업인지 확인하세요! ! ! 그렇지 않으면 잠금이 해제되지 않는 문제가 계속 발생할 수 있습니다.

이 솔루션에는 허점이 있습니다.

  • 공유 리소스를 작동하는 시간이 만료 시간보다 길면 잠금이 조기에 만료되어 분산 잠금이 직접 실패하게 됩니다.

  • 잠금 시간 초과가 설정되면 성능에 영향을 미칩니다

3. 잠금을 정상적으로 갱신하는 방법

Redisson은 다양한 기본 기능을 제공하는 오픈 소스 Java 언어 Redis 클라이언트입니다. 다중 배포뿐만 아니라 잠금 구현도 포함됩니다. 또한 Redisson은 Redis 독립형, Redis Sentinel 및 Redis Cluster와 같은 여러 배포 아키텍처도 지원합니다.

Redisson의 분산 잠금에는 사용이 매우 간단하고 원리도 비교적 간단합니다. 공유 리소스를 운영하는 경우 특별히 사용되는 Watch Dog(워치 독)을 제공합니다. 스레드가 실행을 완료하지 않은 경우 Watch Dog는 잠금 만료 시간을 지속적으로 연장하여 시간 초과로 인해 잠금이 해제되지 않도록 합니다.

Java 분산 잠금을 구현하는 방법

使用方式举例:

// 1.获取指定的分布式锁对象
RLock lock = redisson.getLock("lock");
// 2.拿锁且不设置锁超时时间,具备 Watch Dog 自动续期机制
lock.lock();
// 3.执行业务
...
// 4.释放锁
lock.unlock();

只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。

// 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
lock.lock(10, TimeUnit.SECONDS);

总的来说就是使用Redisson,它带有自动的续期机制

4. 如何实现可重入锁

所谓可重入锁指的是在一个线程中可以多次获取同一把锁,比如一个线程在执行一个带锁的方法,该方法中又调用了另一个需要相同锁的方法,则该线程可以直接执行调用的方法即可重入 ,而无需重新获得锁。像 Java 中的 synchronized 和 ReentrantLock 都属于可重入锁。

可重入分布式锁的实现核心思路是线程在获取锁的时候判断是否为自己的锁,如果是的话,就不用再重新获取了。为此,我们可以为每个锁关联一个可重入计数器和一个占有它的线程。当可重入计数器大于 0 时,则锁被占有,需要判断占有该锁的线程和请求获取锁的线程是否为同一个。

위 내용은 Java 분산 잠금을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제