redis + candado distribuido

1. ¿Qué es un candado distribuido?

Para introducir bloqueos distribuidos, primero debemos mencionar bloqueos de subprocesos y bloqueos de proceso que corresponden a bloqueos distribuidos.

Bloqueo de hilo: se utiliza principalmente para bloquear métodos y bloques de código. Cuando un método o código usa un bloqueo, solo un subproceso ejecuta el método o segmento de código al mismo tiempo. Los bloqueos de subprocesos solo son efectivos en la misma JVM, porque la realización de bloqueos de subprocesos se logra básicamente compartiendo memoria entre subprocesos. Por ejemplo, sincronizado es el encabezado del objeto compartido, y el bloqueo de pantalla Lock está compartiendo una determinada variable (estado).

Bloqueo de procesos: para controlar múltiples procesos en el mismo sistema operativo para acceder a un recurso compartido, cada proceso no puede acceder a los recursos de otros procesos debido a la independencia de los procesos, por lo que los bloqueos de procesos no se pueden lograr a través de bloqueos de subprocesos como sincronizados.

Bloqueo distribuido: cuando varios procesos no están en el mismo sistema, utilice bloqueos distribuidos para controlar el acceso a los recursos mediante múltiples procesos.

2. Utilice escenarios de bloqueos distribuidos.

Tanto los problemas de simultaneidad entre subprocesos como los problemas de simultaneidad entre procesos pueden resolverse mediante bloqueos distribuidos, ¡pero esto no es recomendable! ¡Porque el uso de bloqueos distribuidos para resolver estos pequeños problemas requiere muchos recursos! Los bloqueos distribuidos deben usarse para resolver el problema de concurrencia de múltiples procesos en una situación distribuida, es la más apropiada.

Existe una situación en la que tanto el hilo A como el hilo B comparten una determinada variable X.

En el caso de una sola máquina (JVM única), memoria compartida entre subprocesos, siempre que el uso de bloqueos de subprocesos pueda resolver el problema de concurrencia.

Si se trata de una situación distribuida (múltiples JVM), el subproceso A y el subproceso B probablemente no estén en la misma JVM, por lo que los bloqueos de subprocesos no funcionarán y se utilizarán bloqueos distribuidos para resolver este problema.

En tercer lugar, la realización de bloqueos distribuidos (Redis)

La clave para la implementación de bloqueos distribuidos es construir un servidor de almacenamiento fuera del servidor de aplicaciones distribuidas para almacenar información de bloqueo En este momento, pensamos fácilmente en Redis. Primero, necesitamos construir un servidor Redis y usar el servidor Redis para almacenar información de bloqueo.

Varios puntos clave a los que se debe prestar atención al implementar:

1. La información de bloqueo debe expirar y expirar, y no se puede permitir que un hilo mantenga un bloqueo durante mucho tiempo y cause un interbloqueo;

2. Solo un hilo puede adquirir el bloqueo al mismo tiempo.

Varios comandos de redis que se utilizarán:

setnx (clave, valor): "establecer si no sale", si el valor-clave no existe, se agrega correctamente a la caché y devuelve 1; de lo contrario, devuelve 0.

get (key): Obtiene el valor correspondiente a la clave y devuelve nil si no existe.

getset (clave, valor): obtenga primero el valor correspondiente a la clave, devuelva nil si no existe y luego actualice el valor anterior al nuevo valor.

expirar (clave, segundos): establece el período de validez del valor-clave en segundos.

Eche un vistazo al diagrama de flujo:

img

Bajo este proceso, no se producirá ningún interbloqueo.

Utilizo Jedis como la API del cliente de Redis, echemos un vistazo al código de implementación específico.

(1) Primero, cree un grupo de conexiones de Redis.

public class RedisPool {
    
    
    private static JedisPool pool; //jedis连接池
    private static int maxTotal = 20; //最大连接数
    private static int maxIdle = 10; //最大空闲连接数
    private static int minIdle = 5; //最小空闲连接数
    private static boolean testOnBorrow = true; //在取连接时测试连接的可用性
    private static boolean testOnReturn = false; //再还连接时不测试连接的可用性
    static {
    
    
        initPool(); //初始化连接池
    }
    public static Jedis getJedis() {
    
    
        return pool.getResource();
    }
    public static void close(Jedis jedis) {
    
    
        jedis.close();
    }
    private static void initPool() {
    
    
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        config.setBlockWhenExhausted(true);
        pool = new JedisPool(config, "127.0.0.1", 6379, 5000, "liqiyao");
    }
}

(2) Encapsular la API de Jedis para encapsular algunas operaciones necesarias para implementar bloqueos distribuidos.

public class RedisPoolUtil {
    
    
    private RedisPoolUtil() {
    
    }
    private static RedisPool redisPool;
    public static String get(String key) {
    
    
        Jedis jedis = null;
        String result = null;
        try {
    
    
            jedis = RedisPool.getJedis();
            result = jedis.get(key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (jedis != null) {
    
    
                jedis.close();
            }
            return result;
        }
    }
    public static Long setnx(String key, String value) {
    
    
        Jedis jedis = null;
        Long result = null;
        try {
    
    
            jedis = RedisPool.getJedis();
            result = jedis.setnx(key, value);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (jedis != null) {
    
    
                jedis.close();
            }
            return result;
        }
    }
    public static String getSet(String key, String value) {
    
    
        Jedis jedis = null;
        String result = null;
        try {
    
    
            jedis = RedisPool.getJedis();
            result = jedis.getSet(key, value);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (jedis != null) {
    
    
                jedis.close();
            }
            return result;
        }
    }
    public static Long expire(String key, int seconds) {
    
    
        Jedis jedis = null;
        Long result = null;
        try {
    
    
            jedis = RedisPool.getJedis();
            result = jedis.expire(key, seconds);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (jedis != null) {
    
    
                jedis.close();
            }
            return result;
        }
    }
    public static Long del(String key) {
    
    
        Jedis jedis = null;
        Long result = null;
        try {
    
    
            jedis = RedisPool.getJedis();
            result = jedis.del(key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (jedis != null) {
    
    
                jedis.close();
            }
            return result;
        }
    }
}

(3) Herramientas de bloqueo distribuidas

public class DistributedLockUtil {
    
    
    private DistributedLockUtil() {
    
    }
    public static boolean lock(String lockName) {
    
     //lockName可以为共享变量名,也可以为方法名,主要是用于模拟锁信息
        System.out.println(Thread.currentThread() + "开始尝试加锁!");
        Long result = RedisPoolUtil.setnx(lockName, String.valueOf(System.currentTimeMillis() + 5000));
        if (result != null && result.intValue() == 1) {
    
    
            System.out.println(Thread.currentThread() + "加锁成功!");
            RedisPoolUtil.expire(lockName, 5);
            System.out.println(Thread.currentThread() + "执行业务逻辑!");
            RedisPoolUtil.del(lockName);
            return true;
        } else {
    
    
            String lockValueA = RedisPoolUtil.get(lockName);
            if (lockValueA != null && Long.parseLong(lockValueA) >= System.currentTimeMillis()) {
    
    
                String lockValueB = RedisPoolUtil.getSet(lockName, String.valueOf(System.currentTimeMillis() + 5000));
                if (lockValueB == null || lockValueB.equals(lockValueA)) {
    
    
                    System.out.println(Thread.currentThread() + "加锁成功!");
                    RedisPoolUtil.expire(lockName, 5);
                    System.out.println(Thread.currentThread() + "执行业务逻辑!");
                    RedisPoolUtil.del(lockName);
                    return true;
                } else {
    
    
                    return false;
                }
            } else {
    
    
                return false;
            }
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/qq_41870170/article/details/115055403
Recomendado
Clasificación