Distributed scene thread-safe solution, redis achieve distributed lock

Practical work, often encounter similar snapped when simultaneous multithreading herein, this description multithreading features a simple grab votes redis distributed lock implementation.


Directly on the code. First By convention, a given error model:


We can look, when taking away 10 tickets with 20 threads when what will happen.


package com.tiger.utils;

 

public class TestMutilThread {

 

// total amount of votes

public static int count = 10;

 

public static void main(String[] args) {

statrtMulti ();

}

 

public static void statrtMulti() {

for (int i = 1; i <= 20; i++) {

TicketRunnable tickrunner = new TicketRunnable();

Thread thread = new Thread(tickrunner, "Thread No: " + i);

thread.start();

}

 

}

 

public static class TicketRunnable implements Runnable {

 

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + "  start "

+ count);

// TODO Auto-generated method stub

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

// + "  really  start" + count);

if (count <= 0) {

System.out.println(Thread.currentThread().getName()

+ "  ticket sold out ! No tickets remained!" + count);

return;

} else {

count = count - 1;

System.out.println(Thread.currentThread().getName()

+ " bought a ticket,now remaining :" + (count));

}

}

}

}

Test results can be seen from the results, the votes have been confusion in a different thread.


Thread No: 2  start 10

Thread No: 6  start 10

Thread No: 4  start 10

Thread No: 5  start 10

Thread No: 3  start 10

Thread No: 9  start 6

Thread No: 1  start 10

Thread No: 1 bought a ticket,now remaining :3

Thread No: 9 bought a ticket,now remaining :4

Thread No: 3 bought a ticket,now remaining :5

Thread No: 12  start 3

Thread No: 5 bought a ticket,now remaining :6

Thread No: 4 bought a ticket,now remaining :7

Thread No: 8  start 7

Thread No: 7  start 8

Thread No: 12 bought a ticket,now remaining :1

Thread No: 14  start 0

Thread No: 6 bought a ticket,now remaining :8

Thread No: 16  start 0

Thread No: 2 bought a ticket,now remaining :9

Thread No: 16  ticket sold out ! No tickets remained!0

Thread No: 14  ticket sold out ! No tickets remained!0

Thread No: 18  start 0

Thread No: 18  ticket sold out ! No tickets remained!0

Thread No: 7 bought a ticket,now remaining :0

Thread No: 15  start 0

Thread No: 8 bought a ticket,now remaining :1

Thread No: 13  start 2

Thread No: 19  start 0

Thread No: 11  start 3

Thread No: 11  ticket sold out ! No tickets remained!0

Thread No: 10  start 3

Thread No: 10  ticket sold out ! No tickets remained!0

Thread No: 19  ticket sold out ! No tickets remained!0

Thread No: 13  ticket sold out ! No tickets remained!0

Thread No: 20  start 0

Thread No: 20  ticket sold out ! No tickets remained!0

Thread No: 15  ticket sold out ! No tickets remained!0

Thread No: 17  start 0

Thread No: 17  ticket sold out ! No tickets remained!0

In order to solve the confusion that occurs when multiple threads, here is the real test class !!!


The real test classes start 20 threads here, taking away 10 tickets.


RedisTemplate redis operation is used to achieve, by the integrated spring. Here it is to use a RedisTemplate, so I configured in the form of an externally RedisTemplate passed to the test class.


MultiTestLock is used to achieve locked tools.


The total number of votes when using the volatile keyword, multi-threaded variable visibility in system memory, which can point to understand the role of lower volatile keyword.


TicketRunnable grab votes for analog functions.


Wherein determining if the presence and UNLOCK between the lock, in order to ensure thread safety, to guarantee synchronized herein.


Test categories:


package com.tiger.utils;

 

import java.io.Serializable;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.data.redis.core.RedisTemplate;

 

 

public class MultiConsumer {

Logger logger=LoggerFactory.getLogger(MultiTestLock.class);

private RedisTemplate<Serializable, Serializable> redisTemplate;

public MultiTestLock lock;

// total amount of votes

public volatile static int count = 10;

 

public void statrtMulti() {

lock = new MultiTestLock(redisTemplate);

for (int i = 1; i <= 20; i++) {

TicketRunnable tickrunner = new TicketRunnable();

Thread thread = new Thread(tickrunner, "Thread No: " + i);

thread.start();

}

 

}

 

public class TicketRunnable implements Runnable {

 

@Override

public void run() {

logger.info(Thread.currentThread().getName() + "  start "

+ count);

// TODO Auto-generated method stub

if (count > 0) {

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

// + "  really  start" + count);

lock.lock();

synchronized (this) {

if(count<=0){

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

+ "  ticket sold out ! No tickets remained!" + count);

lock.unlock();

return;

}else{

count=count-1;

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

+ " bought a ticket,now remaining :" + (count));

}

}

lock.unlock();

}else{

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

+ "  ticket sold out !" + count);

}

}

}

 

public RedisTemplate<Serializable, Serializable> getRedisTemplate() {

return redisTemplate;

}

 

public void setRedisTemplate (

RedisTemplate<Serializable, Serializable> redisTemplate) {

this.redisTemplate = redisTemplate;

}

 

public MultiConsumer(RedisTemplate<Serializable, Serializable> redisTemplate) {

super();

this.redisTemplate = redisTemplate;

}

}

Lock Tools:


We know that in order to ensure thread safety, when the operation must be atomic program execution. Subsequent versions redis set key can be used simultaneously set timeout expire.


Think of the last to be paid telecommunications wing interview, the interviewer asked a question: How Distributed Lock to prevent deadlock, the key issue is that we were successful when operating in a distributed lock in, but when you perform the unlock operation is completed follow-up business failure. Resulting in a distributed lock can not be released. Deadlock, subsequent lock can not be normal. So here Purpose expire timeout is to prevent the emergence of failed unlock, so that even if unlock fails, automatic release after the lock will still be distributed after the timeout.


DETAILED also comments in the code, it can be used as a reference.


package com.tiger.utils;

 

import java.io.Serializable;

import java.util.Arrays;

import java.util.Collections;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

java.util.Random import;

java.util.concurrent.TimeUnit import;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

 

import javax.sound.midi.MidiDevice.Info;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.dao.DataAccessException;

import org.springframework.data.redis.core.RedisOperations;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.SessionCallback;

import org.springframework.data.redis.core.script.RedisScript;

 

 

public class MultiTestLock implements Lock {

Logger logger=LoggerFactory.getLogger(MultiTestLock.class);

private RedisTemplate<Serializable, Serializable> redisTemplate;

public MultiTestLock(RedisTemplate<Serializable, Serializable> redisTemplate) {

super();

this.redisTemplate = redisTemplate;

}

 

@Override

public void lock() {

// here to use a while loop to grab the lock operation performed after the thread is forced to come in. Only grab the key in order to carry out the subsequent operations

while(true){

if(tryLock()){

try {

// this thread to sleep 500 milliseconds purpose is to simulate time-consuming business, ensure that the value set before the end of the business just hit the timeout,

// actual production may be biased, there needs experience

Thread.sleep(500l);

// logger.info(Thread.currentThread().getName()+" time to awake");

return;

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace ();

}

}else{

try {

// while reducing the frequency of cycle during sleep provided a random object here ms 

Thread.sleep(new Random().nextInt(200)+100);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace ();

}

}

}

}

 

@Override

public boolean tryLock() {

// Here you can also choose transactionSupport support transactional operations

SessionCallback<Object> sessionCallback=new SessionCallback<Object>() {

@Override

public  Object execute(RedisOperations operations)

throws DataAccessException {

operations.multi();

operations.opsForValue().setIfAbsent("secret", "answer");

// set the timeout to be based on actual business of processing time may be, is an empirical value

operations.expire("secret", 500l, TimeUnit.MILLISECONDS);

Object object=operations.exec();

return object;

}

};

// perform two operations, there will get a value array [true, true], respectively corresponding to the result of the two operations, the first time Should a false value indicates that the first step set Error

List<Boolean> result=(List) redisTemplate.execute(sessionCallback);

// logger.info(Thread.currentThread().getName()+" try lock "+ result);

if(true==result.get(0)||"true".equals(result.get(0)+"")){

logger.info(Thread.currentThread().getName()+" try lock success");

return true;

}else{

return false;

}

}

 

@Override

public boolean tryLock(long arg0, TimeUnit arg1)

throws InterruptedException {

// TODO Auto-generated method stub

return false;

}

 

@Override

public void unlock() {

// unlock operation directly remove the lock, if you do not complete the timeout period is deleted directly, so that subsequent threads to continue. Acts make up a knife, to ensure that the lock has expired or been deleted

SessionCallback<Object> sessionCallback=new SessionCallback<Object>() {

@Override

public  Object execute(RedisOperations operations)

throws DataAccessException {

operations.multi();

operations.delete("secret");

Object object=operations.exec();

return object;

}

};

Object result=redisTemplate.execute(sessionCallback);

}

 

 

@Override

public void lockInterruptibly() throws InterruptedException {

// TODO Auto-generated method stub

}

 

@Override

public Condition newCondition() {

// TODO Auto-generated method stub

return null;

}

public RedisTemplate<Serializable, Serializable> getRedisTemplate() {

return redisTemplate;

}

 

public void setRedisTemplate (

RedisTemplate<Serializable, Serializable> redisTemplate) {

this.redisTemplate = redisTemplate;

}

 

}

Results of the




It can be seen a steady decline in the number of votes, the follow-up did not grab the lock thread than votes to 0, without a ticket can grab.


tips:


During which there have been a problem, multi Redis of the package, the system being given: ERR EXEC without MULTI


After a review found that the problem lies in:


In the spring, the MULTI repeatedly execute the command will not complain, because the first execution, will be a isInMulti its internal variable set to true, subsequent commands are executed each time checks this variable, if true, do not perform command. The EXEC command will be executed repeatedly said at the beginning of "ERR EXEC without MULTI" error report.


Guess you like

Origin blog.51cto.com/14661022/2464830