IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis笔记(三) -> 正文阅读

[大数据]Redis笔记(三)

对于Redis的学习,除了底层的原理,笔者认为,如何在各个场景中合理的设计使用Redis也是十分的关键的,Redis提供了五种数据类型,可以让我们在更多的场景中做出选择。接下来笔者就从实际案例出发,首先从Redis的分布式锁开始。在日常生活中,我们肯定有接触过抢票(抢商品)的场景,那么这个时候就会存在并发问题,下面的代码主要是在模拟一个抢票的场景。

RedisLockController

@RestController
public class RedisLockController {
    private static long count = 20;
    //CountDownLatch是一个并发工具类,具体的使用可以看我的并发编程章节,有详细讲解常用的并发工具类
    private CountDownLatch countDownLatch = new CountDownLatch(5);
    @Resource(name="redisLock")
    private RedisLock lock;

    @RequestMapping(value = "/sale", method = RequestMethod.GET)
    public Long sale() throws InterruptedException {
        count = 20;
        countDownLatch = new CountDownLatch(5);
        System.out.println("-------五个窗口开售20张票-------");
        new PlusThread().start();
        new PlusThread().start();
        new PlusThread().start();
        new PlusThread().start();
        new PlusThread().start();
        return count;
    }

    // 线程类模拟一个窗口买火车票
    public class PlusThread extends Thread {
        private int amount = 0;//当前线程成功抢了多少张票

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "开始售票");
            countDownLatch.countDown();
            if (countDownLatch.getCount()==0){
                System.out.println("----------结果------------");
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            while (count > 0) {
                //lock方法会自旋尝试获取锁,如果获取失败会递归调用自身lock,直到获取成功
                lock.lock();
                try {
                    if (count > 0) {
                        //模拟卖票业务处理
                        amount++;
                        count--;
                    }
                }finally{
                    lock.unlock();
                }

                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "售出"+ (amount) + "张票");
        }
    }
}

RedisConfig

@Configuration
public class RedisConfig {
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(lettuceConnectionFactory);
        // 这里可以给template加一些序列化的定制
        return template;
    }
}

RedisLock

@Component
public class RedisLock {
    private static final String  KEY = "LOCK_KEY";
    @Resource(name = "redisTemplate")
    private RedisTemplate<String, Object> redisTemplate;
    //使用ThreadLocal这个类是在多线程中数据隔离的一种方式,具体底层原理,在高并发章节会详细讲解到
    private static ThreadLocal<String> local = new ThreadLocal<>();


    //加锁
    public void lock() {
        //1.尝试加锁,成功则直接返回表示当前线程获取锁成功
        if(tryLock()){
            return;
        }
        //2.加锁失败,当前任务休眠一段时间
        try {
            Thread.sleep(10);//这个睡眠时间根据业务需求而定
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //3.睡眠结束,递归调用,再次去抢锁
        lock();
    }



    //尝试获取锁,使用setNx命令返回OK的加锁成功,并生产随机值
    public boolean tryLock() {
        //产生随机值,标识本次锁编号
        String uuid = UUID.randomUUID().toString();

        RedisCallback<Boolean> callback = (connection) -> connection.set(KEY.getBytes(StandardCharsets.UTF_8), uuid.getBytes(StandardCharsets.UTF_8), Expiration.milliseconds(1000L), RedisStringCommands.SetOption.SET_IF_ABSENT);
        boolean re = redisTemplate.execute(callback);
        //设值成功--抢到了锁
        if(re){
            local.set(uuid);//抢锁成功,把锁标识号记录入本线程--- Threadlocal
            return true;
        }

        //key值里面有了,我的uuid未能设入进去,抢锁失败
        return false;
    }

    //解锁
    public boolean unlock() {
        //FileUtils工具类读取lua脚本
        String script = FileUtils.getScript("unlock.lua");
        //执行lua脚本
        RedisCallback<Boolean> callback = (connection) -> connection.eval(script.getBytes(), ReturnType.BOOLEAN ,1, KEY.getBytes(StandardCharsets.UTF_8), local.get().getBytes(StandardCharsets.UTF_8));
        return redisTemplate.execute(callback);
    }

}

FileUtils

@Component
public class FileUtils {
    public static String getScript(String fileName){
        String path = FileUtils.class.getClassLoader().getResource(fileName).getPath();
        return readFileByLines(path);
    }

    public static String readFileByLines(String fileName) {
        FileInputStream file = null;
        BufferedReader reader = null;
        InputStreamReader inputFileReader = null;
        String content = "";
        String tempString = null;
        try {
            file = new FileInputStream(fileName);
            inputFileReader = new InputStreamReader(file, "utf-8");
            reader = new BufferedReader(inputFileReader);
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                content += tempString;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
        return content;
    }
}

unlock.lua

if redis.call("get",KEYS[1]) == ARGV[1] then 
    return redis.call("del",KEYS[1]) 
else 
    return 0 
end

运行结果:

在这里插入图片描述

那么如果我们在这个抢票系统中,不使用锁,会发生怎样的事情呢?我们将锁相关的代码注释掉,运行看看结果:可以发现,二十张票却出售了23张票

在这里插入图片描述

在这里插入图片描述

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-12-13 12:53:31  更:2021-12-13 12:53:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:53:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码