Redis集群和应用——02

redis1(入门)

一、主从复制原理(读写分离)

在这里插入图片描述

原理:第一次同步数据由从服务器发起,当主服务器接收到从服务器要求同数据的请求后,将dump.rdb文件发送给从服务器。之后主服务器每次写操作后,由主服务器发起同步请求。

1、配置一主二从集群

  1. 创建文件夹:
# 6379
[root@iz2zedg4ylq9iqtwm11wecz redis6379]# pwd
/my/redises/redis6379
[root@iz2zedg4ylq9iqtwm11wecz redis6379]# ls
conf  data

#6380
[root@iz2zedg4ylq9iqtwm11wecz redis6380]# pwd
/my/redises/redis6380
[root@iz2zedg4ylq9iqtwm11wecz redis6380]# ls
conf  data
[root@iz2zedg4ylq9iqtwm11wecz redis6380]# 

#6381
[root@iz2zedg4ylq9iqtwm11wecz redis6381]# pwd
/my/redises/redis6381
[root@iz2zedg4ylq9iqtwm11wecz redis6381]# ls
conf  data
[root@iz2zedg4ylq9iqtwm11wecz redis6381]# 

  1. 复制redis.conf

将redis.conf分别复制到三个目录中,并将redis.conf其中的端口号修改为6379、6380、6381

[root@iz2zedg4ylq9iqtwm11wecz conf]# pwd
/my/redises/redis6379/conf
[root@iz2zedg4ylq9iqtwm11wecz conf]# cp /my/redis/conf/redis.conf ./
  1. 使用docker开启三个reids容器
#6379
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker run --name redis6379 -p 6379:6379 --net host -v /my/redises/redis6379/conf/redis.conf:/usr/local/etc/redis/redis.conf -v /my/redises/redis6379/data/:/data -d redis redis-server /usr/local/etc/redis/redis.conf
3c3d9c85cc22ca483eda0da1ead04e7dd58fd0b74438ad4529855f7cd284bb5a
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS           
3c3d9c85cc22        redis               "docker-entrypoint..."   26 seconds ago      Exited (0) 25 sec
dfabf4da2bdd        nginx               "/docker-entrypoin..."   2 months ago        Exited (255) 7 we
598fef497cec        redis               "docker-entrypoint..."   2 months ago        Exited (0) 11 min
78c4a571ae3b        mysql:5.7           "docker-entrypoint..."   2 months ago        Exited (255) 7 we
[root@iz2zedg4ylq9iqtwm11wecz ~]# 

#6380
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker run --name redis6380 -p 6380:6380 --net host -v /my/redises/redis6380/conf/redis.conf:/usr/local/etc/redis/redis.conf -v /my/redises/redis6380/data/:/data -d redis redis-server /usr/local/etc/redis/redis.conf
996d60edaa59d7485920a355e25a362a7b51e0c38e8cd13e92b7b72a68c1b74d

#6381
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker run --name redis6381 -p 6381:6381 --net host -v /my/redises/redis6381/conf/redis.conf:/usr/local/etc/redis/redis.conf -v /my/redises/redis6381/data/:/data -d redis redis-server /usr/local/etc/redis/redis.conf
aab7ed4ad32f6f4944aa95dda085e958a0d342302fa8c73f5886e52734f2e40c
  1. 通过docker start 容器名 启动三个redis容器
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker start redis6379
redis6379
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker start redis6380
redis6380
[root@iz2zedg4ylq9iqtwm11wecz ~]# docker start redis6381
redis6381
[root@iz2zedg4ylq9iqtwm11wecz ~]# 
  1. 进入容器
docker exec -it redis6379 /bin/bash
docker exec -it redis6380 /bin/bash
docker exec -it redis6381 /bin/bash
  1. 通过slaveof ip port 将这个redis所在的主机变为从机

注意:修改了redis的端口号后,在打开redis-cli时需要指定对应的端口号,比如redis-cli -p 6380

# 6380( 这里显示6379是因为dockaer端口映射的原理,这台主机外界需要通过6380这个端口进行访问)
127.0.0.1:6379> slaveof 39.96.52.225 6379
OK
# 6381
127.0.0.1:6379> slaveof 39.96.52.225 6379
OK
  1. 通过命令info replication 查看redis的信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=39.96.52.225,port=6379,state=online,offset=70,lag=0
slave1:ip=39.96.52.225,port=6379,state=online,offset=70,lag=1
master_failover_state:no-failover
master_replid:57e856a00e5642b200b23c9872fa7f0cad475381
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70
127.0.0.1:6379> 

注意:如果失败,原因如下:
1、没有开放端口,包括阿里云
2、redis.conf配置里面没有关闭保护模式和只允许本机访问

2、测试读写分离

注意主机redis能进行读和写,但是从机redis只能就行读,不能写

# 在主机设置一个key,在两个从机中也能获取到响应的key

# 6379
127.0.0.1:6379> hset user id 1 name lihua age 18
(integer) 3

# 6380
127.0.0.1:6379> hgetall user
1) "id"
2) "1"
3) "name"
4) "lihua"
5) "age"
6) "18"

# 6381
127.0.0.1:6379> hgetall user
1) "id"
2) "1"
3) "name"
4) "lihua"
5) "age"
6) "18"

尝试在从机写一些key

127.0.0.1:6379> set 1 1
(error) READONLY You can't write against a read only replica.

3、服务器宕机后

  1. 当从机宕机后,再次启动时
    从服务器不会直接成为主服务器的从服务器,需要再次对从服务器进行加入(slaveof ip port)操作。加入后会从主服务器中从头到尾重新同步数据,并再次成为主服务器的从服务器。
  2. 当主服务器宕机后。
    当主服务器恢复后从服务器还是从服务器。但是主服务器宕机期间,从服务器还是只能有读操作,没有写操作。

二、主从复制——薪火相传(链式的)

在这里插入图片描述
薪火相传也就是,从服务器也能有从服务器。从服务器也可以通过slaveof ip port 添加从服务器

三、反客为主 (主服务器宕机后从服务器变为主服务器)

可以通过命令slaveof no one 将从服务器变为主服务器
注意:
执行slaveof on one命令后,从服务器会变为主服务器。但是这个服务器不在是之前主服务器的从服务器。也就是说这个服务器,将变成主服务器,但是此时它是没有任何的从服务器,也没有成为任何主机的从服务器。

四、哨兵模式(升级版反客为主)

1、创建sentinel.conf文件

在redis容器中创建sentinel.conf文件

root@0bee7c2d98cb:/usr/local/bin# pwd
/usr/local/bin
root@0bee7c2d98cb:/usr/local/bin# touch sentinel.conf
ok
root@0bee7c2d98cb:/usr/local/bin# ls
docker-entrypoint.sh  redis-benchmark  redis-check-rdb	redis-sentinel	sentinel.conf
gosu		      redis-check-aof  redis-cli	redis-server

编写sentinel.conf文件,如果没有vim、vi那么可以在宿主主机中用docker cp将编写好的文件复制到容器中。

sentinel monitor mymastter 39.96.52.225 6379 1
# mymastter:给主机一个名称; 39.96.52.225 6379:主机的IP和端口号,1:表示多少个哨兵同意就能将从机升为主机

也可以这样写入

# 写入
echo "sentinel monitor mymastter 39.96.52.225 6379 1" > sentinel.conf
# 查看
cat sentinel.conf

2、启动哨兵

注意:1、在redis的安装目录启动(也就是有redis-sentinel 文件下)
2、注意不要在主机的容器中启动哨兵,因为等下就是要关闭主机,主机的容器关闭后,哨兵也会被关闭,这样就无法监听了

root@0bee7c2d98cb:/usr/local/bin# pwd
/usr/local/bin
root@0bee7c2d98cb:/usr/local/bin# ls
docker-entrypoint.sh  redis-benchmark  redis-check-rdb	redis-sentinel	sentinel.conf
gosu		      redis-check-aof  redis-cli	redis-server
root@0bee7c2d98cb:/usr/local/bin#
# 运行
redis-sentinel sentinel.conf

在这里插入图片描述

3、然后通过docker stop 关闭主reids

docker stop redis6379

主机被关闭后哨兵就会自动选择一台从机作为主机,并且之前的主机会变为现在主机的从机。当之前的主机重新运行时依旧会作为新主机的从机

当从机变为主机后,会将以前主机拥有的从机变为自己的从机,以前的主机也会变成自己的从机

在这里插入图片描述

4、主机挂掉后如何选举主机呢?

  1. 首先根据优先级 在reids.conf 中配置 replica-priority 100
    值越小优先级越高
  2. 当优先级一样,选择从主机中同步到的数据最全的(看key的个数)
  3. 根据redis运行后随机生成的 的runid,越小越优先

五、搭建redis集群

在这里插入图片描述

1、并发写操作(之前的主从复制,只能有一台主机有写功能。搭建redis集群后可以实现多台服务器有写功能。)
2、统一调用接口(并且之前的服务器必须通过不同的ip和端口号进行访问。不能进行统一ip端口进行访问。)
3、rdis集群实现了内存扩容

1、修改redis.conf文件

# 修改redis.conf文件
[root@iz2zedg4ylq9iqtwm11wecz conf]# pwd
/my/redises/redis6379/conf
[root@iz2zedg4ylq9iqtwm11wecz conf]# ls
redis.conf
[root@iz2zedg4ylq9iqtwm11wecz conf]# vim redis.conf 

cluster-enabled yes  #开始集群
 #集群节点的配置文件名,注意这个配置文件不是启动redis的配置文件,而是在运行后自动生成的集群节点的配置文件
cluster-config-file nodes-6379.conf 
cluster-node-timeout 15000 #改节点的超时时间,超时自动切换节点

在这里插入图片描述

2、修改redis的默认端口号

在redis.conf 里面就行修改就可以了

# 运行redis客户端
redis-cli -p 6380

注意修改节点的端口号后在连接redis-cli时要指定端口号

3、运行redis后nodes-6379.conf 会在工作目录生成


root@74558bbeeb6e:/data# ls
dump.rdb  nodes-6379.conf

root@e763b91a444c:/data# ls
dump.rdb  nodes-6380.conf

root@35adf3a37c72:/data# ls
dump.rdb  nodes-6381.conf

注意:请确保节点配置文件都生成

4、将节点合成集群

使用docker安装redis,在运行容器时指定的安装目录为:
root@0bee7c2d98cb:/usr/local/bin# pwd
/usr/local/bin

创建集群

# 在改目录下运行
root@74558bbeeb6e:/usr/local/bin# pwd
/usr/local/bin
root@74558bbeeb6e:/usr/local/bin# redis-cli --cluster create 39.96.52.225:6379 39.96.52.225:6380 39.96.52.225:6381

注意:如果运行失败注意redis的版本
失败参考

我的redis版本为:

root@74558bbeeb6e:/usr/local/bin# redis-server --version redis-server -v
Redis server v=6.2.3 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=dc20d908b7b619b4

生成集群时可能会报以下错误

[ERR] Node 39.96.52.225:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

解决方法

成功如下
在这里插入图片描述

如果一直等待加入,可能需要修改
1、开放端口号,如果节点的端口为6379 ,那么需要开放6379-16379。比如节点中最大的端口为6389,那么要开放6379-16389
参考
2、如果使用docker搭建的redis,需要在创建docker容器创建时允许联网
解决:创建容器时添加–net host 即可如下

docker run --name redis6379 -p 6379:6379 --net host -v /my/redises/redis6379/conf/redis.conf:/usr/local/etc/redis/redis.conf -v /my/redises/redis6379/data/:/data -d redis redis-server /usr/local/etc/redis/redis.conf

在这里插入图片描述

5、连接redis集群

注意:通过任意端口都能连接集群,连接集群后能操作集群里面的所有节点

# 使用 redis-cli -c -p 6379 连接集群,-c 表示连接集群 -p 表示端口号,任意节点的端口号都可以
root@iz2zedg4ylq9iqtwm11wecz:/data# redis-cli -c -p 6379
127.0.0.1:6379>

# 当然你也可以使用redis-cli 连接单个节点,这样就没有连接上集群
root@iz2zedg4ylq9iqtwm11wecz:/data# redis-cli -p 6380
127.0.0.1:6380>

# 查看集群的节点
127.0.0.1:6379> cluster nodes
098bd49903e178ddcca36222b7227602c9c5d63e 39.96.52.225:6380@16380 master - 0 1629266807819 2 connected 5461-10922
c75a4531edba630b7445a70b5f48a89fc8e64d3c 39.96.52.225:6381@16381 master - 0 1629266807000 3 connected 10923-16383
a3b57cd2eeb9d7b049d76668c68e4394e96f6164 172.24.12.28:6379@16379 myself,master - 0 1629266803000 1 connected 0-5460

在set key时集群会自动根据key,分配给各个节点进行存储

127.0.0.1:6379> set 11 11
OK
127.0.0.1:6379> set 1122 11
-> Redirected to slot [7470] located at 39.96.52.225:6380
OK
39.96.52.225:6380> set 22 11
-> Redirected to slot [13798] located at 39.96.52.225:6381
OK
39.96.52.225:6381>

6、集群中如何分配节点(那些节点成为主节点,那些节点为从节点,从节点怎么认主)

原则:分配是保证每个主节点运行在不同的ip(服务器)上,主节点和从节点不在同一个ip(服务器上)。目的是防止一台服务器挂掉后,集群受到的影响最小。

7、如何存储key(负载均衡)

在这里插入图片描述
根据key的值,利用算法,计算出一个范围在0——16384的slot值,再根据这个slot值确定由哪个节点存储
在这里插入图片描述

8、redis集群常见操作

# 根据k2计算出slot,根据slot 的值确定将这个值存到6379这个节点
39.96.52.225:6381> set k2 v1 
-> Redirected to slot [449] located at 39.96.52.225:6379
OK
39.96.52.225:6379> get 11
"11"
# 根据k1计算出slot,根据slot 的值找到key在6379这个节点里
39.96.52.225:6379> get k1
-> Redirected to slot [12706] located at 39.96.52.225:6381
"v1"
# 计算key的slot值
39.96.52.225:6381> cluster keyslot k1
(integer) 12706

9、redis集群存在的一些问题

  1. mset 指令与单节点不同
# 使用单节点的指令会报错
39.96.52.225:6381> mset 1 1 2 2 3 3 4 4
(error) CROSSSLOT Keys in request don't hash to the same slot

原因:因为这里的指令mset有多个key,redis的负载均衡算法无法计算出来这些key的范围
解决:给这些key分组

39.96.52.225:6381> mset 1{num} 1 2{num} 2 3{num} 3 4{num} 4
-> Redirected to slot [2765] located at 39.96.52.225:6379
OK

10、故障恢复

主节点宕机后,有两种情况
1、若有从节点,那么从节点会自动升级变为主节点,当以前的主节点恢复后,会成为新主节点的从节点。
2、若没有从节点(或者主节点和从节点都宕机),那么redis的默认配置是,整个集群都不可用。可以通过修改redis.conf的参数配置 cluster-require-full-coverage yes 进行配置 。yes为整个集群不可用。

11、jedis使用redis集群

/**
 * reis集群测试
 * @author 15594
 */
public class RedisCluster {
    public static void main(String[] args) {
        //创建集群操作对象
        //端口号可以时集群节点中的任意一个
        HostAndPort hostAndPort = new HostAndPort("39.96.52.225", 6381);
        JedisCluster jedisCluster = new JedisCluster(hostAndPort);

        jedisCluster.set("b1","value1");

        String b1 = jedisCluster.get("b1");
        System.out.println(b1);


    }
}

六、redis应用问题

1、缓存穿透——访问不存在的key

请求获取数据的原则是,先访问redis缓存,redis中不存在所需要的数据,就去访问数据库。

  1. 什么是缓存穿透
    当非正常的请求(url)恶意访问redis中不存在的key,使得频繁访问数据库。
  2. 解决方案:
  • 对空值就行缓存。在查询数据库后,对不存在的key也加入redis缓存,但是要设置过期时间。
  • 设置白名单。限制ip访问,或则限制访问次数。
  • 如果redis缓存命中率降低,那么就是核查

可以通过布隆过滤器解决缓存穿透问题。

布隆过滤器详解 看了就会用

2、缓存击穿——某个热门的key过期了

  1. 什么是缓存击穿
    瞬时大量请求访问到某个过期的key,然后这些请求瞬时访问数据库,照成数据库崩溃。
  2. 解决方案
  • 预先设置热门数据
  • 根据访问量增加过期时间

3、缓存雪崩——访问到大量过期的key

  1. 什么是缓存雪崩
    访问到大量过期的key,造成大量的请求访问到数据库,造成服务雪崩
  2. 解决方案
  • 使用多级缓存架构 (nginx缓存+redis缓存+其他缓存)
  • 使用锁或者队列(当大量key失效时,对需要直接访问数据库的请求进行加锁或者加入队列,减轻数据库访问压力)
  • 设置过期标志更新缓存。(未雨绸缪,提前预知key的过期时间,提前更新key)
  • 分散key的过期时间。(不要集中设置key的过期时间)

七、分布式锁

与单机的锁不一样,分布式的锁,对不同的主机是不可见的。因为你不能跨操作系统。单机的锁由jvm控制。

分布式锁是解决分布式环境下集群对共享资源的操作。

synchronized它不香吗为啥还要用分布式锁?

  1. 不加锁
    在这里插入图片描述

  2. 加synchronized、lock锁

在这里插入图片描述
3. 当系统升级变为分布式的集群
在这里插入图片描述

  1. 解决分布式集群操作共享资源问题——使用分布式锁

在这里插入图片描述

1、redis如何实现

  1. 怎么上锁
# setnx——当key不存在时才能添加key
127.0.0.1:6379> setnx 2 2
(integer) 1
  1. 怎么解锁
# 删除这个key,其他请求才能setnx 这个key(上锁)
127.0.0.1:6379> del 2
(integer) 1
  1. 只是上锁可能会形成死锁
    解决:添加超时时间。超时就自动释放锁,也就是给key设置一个过期时间
# nx——不存在时才添加key ,px设置过期时间 以毫秒为单位 ex是以秒为单位
127.0.0.1:6379> set 3 3 nx px 5000
OK

注意:这里不能通过先setnx key value,在给key设置一个过期时间。因为这样分两条指令执行,在分布式中可能会被打断,照成无法成功设置过期时间

2、jedis实现

2.1、不使用lua脚本(在进行if判断时可能会被自动过期指令打断)
package com.lihua.springbootredis.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁——redis实现
 * @author 15594
 */
@RestController
public class ReidLockController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/testLock")
    public void testLock(){

        //分布式中一般用雪花算法获取id
        UUID uuid = UUID.randomUUID();
        String uid = uuid.toString();

        System.out.println(uid);

        //1、获取锁,相当于setnx,如果能设置成功,返回true,成功获取锁,否则失败             3:过期时间值  TimeUnit.SECONDS 时间单位
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uid,3, TimeUnit.SECONDS);
        //2、如果获取到锁了,就能操作共享资源
        if (lock){
            Integer num = (Integer)redisTemplate.opsForValue().get("num");
            if (num==null){
                System.out.println("num为null");
                return;
            }
            if (num==0){
                return;
            }
            String isMyLock= (String) redisTemplate.opsForValue().get("lock");
            System.out.println(isMyLock);
            //进行--是也要判断自己的锁是否已近过期被释放并且被其他客户端获取,//isMyLock==null&&isMyLock.equals(uid) 防止代码执行到这里key过期了
            if (num>0&&isMyLock!=null&&isMyLock.equals(uid)){
                System.out.println("num--");
                //这里依旧会出问题,因为key可能在进行了if判断后过期,所有要通过lua脚本保证判断和修改同时就行,不会被打断(自动过期的指令打断)
                redisTemplate.opsForValue().decrement("num");
            }
            //防止释放错别人的锁(锁key过期后,自动释放锁,锁又被其他请求上锁,如果不去判断是不是自己的锁就会误释放别人的锁)
            if(redisTemplate.opsForValue().get("lock").equals(uid)){
                //这里依旧会出问题,因为key可能在进行了if判断后过期,所有要通过lua脚本保证判断和修改同时就行,不会被打断(自动过期的指令打断)
                System.out.println("释放锁");
                redisTemplate.delete("lock");
            }
            System.out.println(redisTemplate.opsForValue().get("num"));
        }else {
            //3、获取锁失败等待100毫秒再次尝试获取锁
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
}


2.2、使用lua脚本(保证了判断与执行的原子性)
package com.lihua.springbootredis.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁——redis实现
 * @author 15594
 */
@RestController
public class ReidLockController {
    @Autowired
    private RedisTemplate redisTemplate;

   

    @GetMapping("/testLockLua")
    public void testLockLua(){

        //分布式中一般用雪花算法获取id
        UUID uuid = UUID.randomUUID();
        String uid = uuid.toString();

        System.out.println(uid);

        //1、获取锁,相当于setnx,如果能设置成功,返回true,成功获取锁,否则失败             3:过期时间值  TimeUnit.SECONDS 时间单位
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uid,3, TimeUnit.SECONDS);
        //2、如果获取到锁了,就能操作共享资源
        if (lock){
            Integer num = (Integer)redisTemplate.opsForValue().get("num");
            if (num==null){
                return;
            }

            if (num==0){
                return;
            }
            String script = "local i=redis.call('get','num')\n" +
                    "\n" +
                    "if tonumber(i)<=0 then \n" +
                    "    return 0;\n" +
                    "end\n" +
                    "if redis.call('exists',KEYS[1])==0 then \n" +
                    "    return 1;\n" +
                    "end\n" +
                    "if redis.call('get',KEYS[1])==ARGV[1] then\n" +
                    "    redis.call('decr','num');\n" +
                    "end\n" +
                    "return 2;";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            //设置lua脚本返回值
            redisScript.setResultType(Long.class);
            redisScript.setScriptText(script);
            // 执行脚本                                           KEYS      ARGV
            redisTemplate.execute(redisScript, Arrays.asList("lock"), uid);

            System.out.println(redisTemplate.opsForValue().get("num"));
            String script1 = "if redis.call('exists',KEYS[1])==0 then\n" +
                    "    return 0;\n" +
                    "end    \n" +
                    "if redis.call('get',KEYS[1])==ARGV[1] then\n" +
                    "    redis.call('del',KEYS[1])\n" +
                    "    return 1;\n" +
                    "else\n" +
                    "    return 0;\n" +
                    "end";
            redisScript.setScriptText(script1);
            redisTemplate.execute(redisScript,Arrays.asList("lock"),uid);
            System.out.println("锁已近释放");
        }else {
            //3、获取锁失败等待100毫秒再次尝试获取锁
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
}

注意:在高并发环境下,if判断是不安全的。遇到if条件判断需要谨慎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值