분산 장면 스레드 안전 솔루션, 레디 스 분산 달성 할 잠금

실제 작품은 종종 유사한 동시 여기에 멀티 스레딩 때 스냅가 발생,이 설명 멀티 스레딩 잠금 구현을 분산 레디 스 간단한 잡아 표를 제공합니다.


직접 코드에. 처음으로 대회, 주어진 오차 모델 :


무슨 일이 일어날 때 20 개 스레드 (10) 티켓을 멀리 복용 할 때 우리는 볼 수있다.


com.tiger.utils 패키지;

 

공용 클래스 TestMutilThread {

 

// 투표의 총량

공공 정적 INT 수 = 10;

 

공공 정적 무효 메인 (문자열 []에 args) {

statrtMulti ();

}

 

공공 정적 무효 statrtMulti () {

위한 (INT 난 = 1; I <= 20; I ++) {

TicketRunnable tickrunner 새로운 TicketRunnable을 () =;

스레드 스레드 = 새 스레드 (tickrunner은, "아니 스레드 :"+ I);

thread.start ();

}

 

}

 

공공 정적 클래스 TicketRunnable 구현의 Runnable {

 

@우세하다

공공 무효 실행 () {

에서 System.out.println (는 Thread.currentThread (). getName () + "시작"

+ 카운트);

// TODO 자동 생성 방법 스텁

// logger.info (는 Thread.currentThread (). getName ()

// +) + 계산 "정말 시작";

경우 (횟수 <= 0) {

에서 System.out.println (는 Thread.currentThread (). getName ()

+ "매진 티켓! 없음 티켓은 남아 없습니다!" + 카운트);

반환;

} 다른 {

계수 개수 = - 1;

에서 System.out.println (는 Thread.currentThread (). getName ()

+ "이제 남은 티켓을 구입 :"+ (횟수));

}

}

}

}

테스트 결과가 결과에서 볼 수, 투표는 다른 스레드에 혼란이왔다.


아니 스레드 : 2 10 시작

아니 스레드 : 6 10 시작

아니 스레드 : 4 10 시작

아니 스레드 : 5 10 시작

아니 스레드 : 3 (10)을 시작

아니 스레드 : 9 6 시작

아니 스레드 : 1 10 시작

아니 스레드 : 1 지금 남아있는 티켓을 구입 : 3

아니 스레드 : 9 이제 남은 티켓을 구입 : 4

아니 스레드 : 3 이제 남은 티켓을 구입 : 5

아니 스레드 : 12 3 시작

아니 스레드 : 5 이제 남은 티켓을 구입 : 6

아니 스레드 : 4 이제 남은 티켓을 구입 : 7

아니 스레드 : 8 7 시작

아니 스레드 : 7 8 시작

아니 스레드 : 12 이제 남은 티켓을 구입 : 1

아니 스레드 : 14 0 시작

아니 스레드 : 6 이제 남은 티켓을 구입 : 8

아니 스레드 : 16 0 시작

아니 스레드 : 2 이제 남은 티켓을 구입 : 9

아니 스레드 : 16 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 14 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 18 0 시작

아니 스레드 : 18 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 7 이제 남은 티켓을 구입 : 0

아니 스레드 : 15 0 시작

아니 스레드 : 8 이제 남은 티켓을 구입 : 1

아니 스레드 : 13 2 시작

아니 스레드 : 19 0 시작

아니 스레드 : 11 3 시작

아니 스레드 : 11 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 10 3 시작

아니 스레드 : 10 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 19 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 13 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 20 0 시작

아니 스레드 : 20 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 15 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

아니 스레드 : 17 0 시작

아니 스레드 : 17 티켓이 매진! 어떤 티켓은 남아 없습니다! 0

때 여러 스레드를 발생하는 혼란을 해결하기 위해, 여기에 실제 테스트 클래스입니다!


실제 테스트 클래스는 10 표를 데려가, 여기에 20 개 스레드를 시작합니다.


RedisTemplate 레디 스 동작 집적 스프링 달성하기 위해 사용된다. 외부 I RedisTemplate 테스트 클래스에 전달 된 형태로 구성되어 있으므로 여기는 RedisTemplate을 사용하는 것이다.


MultiTestLock가 잠겨 도구를 달성하는 데 사용됩니다.


휘발성 키워드를 사용하여 총 투표 수는 낮은 휘발성 키워드의 역할을 이해하기 위해 가리킬 수 있습니다 시스템 메모리 변수 가시성을, 멀티 스레드.


아날로그 기능을위한 TicketRunnable 잡아 표.


보증에 스레드 안전성을 보장하기 위해 로크의 현재 상태 및 잠금 해제, 본원 동기화되는지를 결정하는 방법.


시험 종류 :


com.tiger.utils 패키지;

 

수입 java.io.Serializable을;

 

수입 org.slf4j.Logger;

수입 org.slf4j.LoggerFactory;

수입 org.springframework.data.redis.core.RedisTemplate;

 

 

공용 클래스 MultiConsumer {

로거 로거 = LoggerFactory.getLogger (MultiTestLock.class);

개인 RedisTemplate <직렬화 직렬화> redisTemplate;

공공 MultiTestLock는 잠금;

// 투표의 총량

공용 휘발성 정적 INT 수 = 10;

 

공공 무효 statrtMulti () {

잠금을 새로운 MultiTestLock = (redisTemplate);

위한 (INT 난 = 1; I <= 20; I ++) {

TicketRunnable tickrunner 새로운 TicketRunnable을 () =;

스레드 스레드 = 새 스레드 (tickrunner은, "아니 스레드 :"+ I);

thread.start ();

}

 

}

 

공용 클래스 TicketRunnable 구현의 Runnable {

 

@우세하다

공공 무효 실행 () {

logger.info (는 Thread.currentThread (). getName () + "시작"

+ 카운트);

// TODO 자동 생성 방법 스텁

경우 (카운트> 0) {

// logger.info (는 Thread.currentThread (). getName ()

// +) + 계산 "정말 시작";

() lock.lock;

동기 (이) {

경우 (횟수 <= 0) {

logger.info (는 Thread.currentThread (). getName ()

+ "매진 티켓! 없음 티켓은 남아 없습니다!" + 카운트);

() lock.unlock;

반환;

}그밖에{

카운트 카운트 = 1;

logger.info (는 Thread.currentThread (). getName ()

+ "이제 남은 티켓을 구입 :"+ (횟수));

}

}

() lock.unlock;

}그밖에{

logger.info (는 Thread.currentThread (). getName ()

+ "티켓 매진!" + 카운트);

}

}

}

 

공개 RedisTemplate <직렬화 직렬화> getRedisTemplate () {

redisTemplate를 반환;

}

 

공공 무효 setRedisTemplate (

RedisTemplate <직렬화 직렬화> redisTemplate) {

this.redisTemplate = redisTemplate;

}

 

공개 MultiConsumer (RedisTemplate <직렬화 직렬화> redisTemplate) {

감독자();

this.redisTemplate = redisTemplate;

}

}

잠금 도구 :


우리는 작업이 원자 프로그램 실행해야 할 경우 순서, 스레드 안전을 보장하는 것을 알고있다. 후속 버전 레디 스 설정 키를 동시에 사용 만료 시간 제한을 설정할 수 있습니다.


통신 날개 인터뷰를 지불 할 마지막 생각, 면접관이 질문을했다 : 교착 상태를 방지하기 위해 잠금을 분산하는 방법, 핵심 문제에 분산 잠금에서 동작 할 때 우리는 성공했지만, 당신이 수행 할 때 잠금 해제 작업이 후속 사업을 완료한다는 것입니다 실패했습니다. 분산 잠금을 초래하는 것은 해제 할 수 없습니다. 교착 상태는 이후의 잠금 정상이 될 수 없습니다. 그래서 여기 목적 잠금 해제가 실패하는 경우에도 잠금 후 자동 해제가 여전히 타임 아웃 후 배포됩니다 그래서, 타임 아웃이 실패 잠금 해제의 출현을 방지하는 것입니다 만료됩니다.


코드에서 또한 상세한으로 코멘트, 그것은 참고 자료로 사용할 수 있습니다.


com.tiger.utils 패키지;

 

수입 java.io.Serializable을;

수입 java.util.Arrays;

수입 Collections의;

수입 java.util.HashMap에;

수입 java.util.Iterator를;

수입은 java.util.List;

java.util.Random의 가져 오기;

java.util.concurrent.TimeUnit을 가져 오기;

수입 java.util.concurrent.locks.Condition;

수입 java.util.concurrent.locks.Lock;

 

수입 javax.sound.midi.MidiDevice.Info;

 

수입 org.slf4j.Logger;

수입 org.slf4j.LoggerFactory;

수입 않는 org.springframework.dao.DataAccessException;

수입 org.springframework.data.redis.core.RedisOperations;

수입 org.springframework.data.redis.core.RedisTemplate;

수입 org.springframework.data.redis.core.SessionCallback;

수입 org.springframework.data.redis.core.script.RedisScript;

 

 

공용 클래스 MultiTestLock 구현 잠금 {

로거 로거 = LoggerFactory.getLogger (MultiTestLock.class);

개인 RedisTemplate <직렬화 직렬화> redisTemplate;

공개 MultiTestLock (RedisTemplate <직렬화 직렬화> redisTemplate) {

감독자();

this.redisTemplate = redisTemplate;

}

 

@우세하다

공공 무효 잠금 () {

// 여기에 스레드가 와서 강제로 후 잠금 작업이 수행 잡아 동안 루프를 사용합니다. 만 후속 작업을 수행하기 위해 키를 잡아

동안 (사실) {

경우 (설정된 tryLock ()) {

{시도

// 수면 500 밀리 초 목적이 스레드가 시뮬레이션 시간이 소요되는 사업이며, 사업의 종료 이전에 설정 한 값이 바로 타임 아웃을 칠 수 있도록

실제 생산이 편향 될 수 있습니다 // 경험이 필요하다

Thread.sleep를 (500리터);

// logger.info (.는 Thread.currentThread () getName () + "깨어 시간");

반환;

} 캐치 (예외 : InterruptedException 전자) {

// TODO 자동 생성 된 catch 블록

e.printStackTrace ();

}

}그밖에{

{시도

// 여기에 제공된 수면 중에 MS를 임의의 객체를주기의 빈도를 줄일 수 

Thread.sleep를 (새로운 랜덤 () nextInt (200) 100.);

} 캐치 (예외 : InterruptedException 전자) {

// TODO 자동 생성 된 catch 블록

e.printStackTrace ();

}

}

}

}

 

@우세하다

공공 부울 설정된 tryLock () {

// 여기에 당신은 또한 transactionSupport 지원 트랜잭션 작업을 선택할 수 있습니다

SessionCallback <개체> sessionCallback = 새로운 SessionCallback <개체> () {

@우세하다

공공 오브젝트 (RedisOperations 작업)을 실행

발생 DataAccessException과 {

operations.multi ();

operations.opsForValue () setIfAbsent ( "비밀", "대답.");

// 될 수 처리 시간이 실제 비즈니스에 기초하는 타임 아웃을 설정 경험적 ​​값은

operations.expire ( "비밀", 500리터, TimeUnit.MILLISECONDS);

Object 객체 = operations.exec ();

객체를 반환;

}

};

// 두 가지 작업을 수행 할 가치 어레이 [TRUE 사실, 각각 두 동작의 결과에 대응이 얻을 처음 거짓 값을 나타내는 것을 제 스텝 설정 오류

목록 <불> 결과 = (목록) redisTemplate.execute (sessionCallback);

// logger.info (는 Thread.currentThread () getName () + "시도 잠금"+ 결과.);

경우 (참 ==의 result.get (0) || "true"로 .equals (result.get (0) + "")) {

logger.info (.는 Thread.currentThread () getName () + "시도 잠금 성공");

true를 반환;

}그밖에{

false를 반환;

}

}

 

@우세하다

공공 부울 설정된 tryLock (긴 것들은 arg0, TimeUnit와의 ARG1)

던져 예외 : InterruptedException {

// TODO 자동 생성 방법 스텁

false를 반환;

}

 

@우세하다

공공 무효 잠금 해제 () {

당신이 제한 시간을 직접 삭제 완료하지 않은 경우 // 잠금 해제 작업이 직접 다음 스레드가 계속 그래서, 잠금 장치를 제거합니다. 사도 행전은 잠금이 만료되었거나 삭제 된 것을 확인하기 위해, 칼을 만들어

SessionCallback <개체> sessionCallback = 새로운 SessionCallback <개체> () {

@우세하다

공공 오브젝트 (RedisOperations 작업)을 실행

발생 DataAccessException과 {

operations.multi ();

operations.delete ( "비밀");

Object 객체 = operations.exec ();

객체를 반환;

}

};

개체 결과 redisTemplate.execute (sessionCallback를) =;

}

 

 

@우세하다

공공 무효 lockInterruptibly ()이 예외 : InterruptedException를 {던졌습니다

// TODO 자동 생성 방법 스텁

}

 

@우세하다

공공 조건 newCondition () {

// TODO 자동 생성 방법 스텁

NULL을 반환;

}

공개 RedisTemplate <직렬화 직렬화> getRedisTemplate () {

redisTemplate를 반환;

}

 

공공 무효 setRedisTemplate (

RedisTemplate <직렬화 직렬화> redisTemplate) {

this.redisTemplate = redisTemplate;

}

 

}

결과




그것은 투표 수의 꾸준한 감소를 볼 수있다, 티켓없이 0 표보다 잠금 실을 잡아하지 않았다 후속은 잡아 수 있습니다.


팁 :


하는 동안 문제, 패키지의 멀티 레디 스가 있었다는 시스템이 주어지고 : ERR EXEC MULTI없이


리뷰는에 문제가 거짓말 것을 발견 한 후 :


스프링에서 MULTI 반복적 제 실행하기 때문에, 진정한 경우, 수행되지 않을 때마다 확인이 변수를 실행 사실, 후속 명령에 내부 변수 집합 isInMulti 것이다 명령 불평되지 수행 명령. EXEC 명령을 반복적으로 실행 "ERR EXEC MULTI없이"오류 보고서의 시작 부분이라고 할 것이다.


추천

출처blog.51cto.com/14661022/2464830