Redis 分布式锁实现

2023-11-19

原文地址:http://blog.csdn.net/zhu_tianwei/article/details/44927331



Redis是一个key-value存储系统。和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

    下面我们以redis为基础设计分布式锁。

1.锁接口定义

  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.List;  
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. /** 
  7.  *锁接口定义 
  8.  */  
  9. public interface IRedisLockHandler {  
  10.       
  11.     /** 
  12.      * 获取锁  如果锁可用   立即返回true,  否则返回false  
  13.      * @param key 
  14.      * @return 
  15.      */  
  16.     boolean tryLock(String key);  
  17.     /** 
  18.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false  
  19.      * @param key 
  20.      * @param timeout 
  21.      * @param unit 
  22.      * @return 
  23.      */  
  24.     boolean tryLock(String key, long timeout, TimeUnit unit);  
  25.       
  26.     /** 
  27.      * 如果锁空闲立即返回   获取失败 一直等待  
  28.      * @param key 
  29.      */  
  30.     void lock(String key);  
  31.       
  32.     /** 
  33.      * 批量获取锁  如果全部获取   立即返回true, 部分获取失败 返回false  
  34.      * @param keyList 
  35.      * @return 
  36.      */  
  37.     boolean tryLock(List<String> keyList);  
  38.       
  39.     /** 
  40.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false  
  41.      * @param keyList 
  42.      * @param timeout 
  43.      * @param unit 
  44.      * @return 
  45.      */  
  46.     boolean tryLock(List<String> keyList, long timeout, TimeUnit unit);  
  47.       
  48.       
  49.     /** 
  50.      * 释放锁 
  51.      * @param key 
  52.      */  
  53.     void unLock(String key);  
  54.     /** 
  55.      * 批量释放锁  
  56.      * @param keyList 
  57.      */  
  58.     void unLock(List<String> keyList);  
  59. }  
2.锁接口实现
  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.CopyOnWriteArrayList;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. import redis.clients.jedis.Jedis;  
  12. import redis.clients.jedis.JedisPool;  
  13. import redis.clients.jedis.Pipeline;  
  14. import redis.clients.jedis.exceptions.JedisConnectionException;  
  15.   
  16. /** 
  17.  * redis 分布式锁实现 
  18.  * 
  19.  */  
  20. public class RedisLockHandler implements IRedisLockHandler {  
  21.   
  22.     private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockHandler.class);  
  23.   
  24.     // 单个锁有效期  
  25.     private static final int DEFAULT_SINGLE_EXPIRE_TIME = 30;  
  26.     // 批量锁有效期  
  27.     private static final int DEFAULT_BATCH_EXPIRE_TIME = 60;  
  28.   
  29.     private final JedisPool jedisPool;  
  30.   
  31.     /** 
  32.      * 构造 
  33.      */  
  34.     public RedisLockHandler(JedisPool jedisPool) {  
  35.         this.jedisPool = jedisPool;  
  36.     }  
  37.       
  38.     /** 
  39.      * 获取锁 如果锁可用 立即返回true, 否则返回false,不等待 
  40.      *  
  41.      * @return 
  42.      */  
  43.     @Override  
  44.     public boolean tryLock(String key) {  
  45.         return tryLock(key, 0L, null);  
  46.     }  
  47.   
  48.     /** 
  49.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
  50.      *  
  51.      * @param timeout 
  52.      * @param unit 
  53.      * @return 
  54.      */  
  55.     @Override  
  56.     public boolean tryLock(String key, long timeout, TimeUnit unit) {  
  57.         Jedis jedis = null;  
  58.         try {  
  59.             jedis = getResource();  
  60.             //系统计时器的当前值,以毫微秒为单位。  
  61.             long nano = System.nanoTime();  
  62.             do {  
  63.                 LOGGER.debug("try lock key: " + key);  
  64.                 //将 key 的值设为 value 1成功  0失败  
  65.                 Long i = jedis.setnx(key, key);  
  66.                 if (i == 1) {  
  67.                     //设置过期时间  
  68.                     jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
  69.                     LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
  70.                     //成功获取锁,返回true  
  71.                     return Boolean.TRUE;  
  72.                 } else { // 存在锁,循环等待锁  
  73.                     if (LOGGER.isDebugEnabled()) {  
  74.                         String desc = jedis.get(key);  
  75.                         LOGGER.debug("key: " + key + " locked by another business:" + desc);  
  76.                     }  
  77.                 }  
  78.                 if (timeout <= 0) {  
  79.                     //没有设置超时时间,直接退出等待  
  80.                     break;  
  81.                 }  
  82.                 Thread.sleep(300);  
  83.             } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
  84.             return Boolean.FALSE;  
  85.         } catch (JedisConnectionException je) {  
  86.             LOGGER.error(je.getMessage(), je);  
  87.             //释放资源  
  88.             returnBrokenResource(jedis);  
  89.         } catch (Exception e) {  
  90.             LOGGER.error(e.getMessage(), e);  
  91.         } finally {  
  92.             returnResource(jedis);  
  93.         }  
  94.         return Boolean.FALSE;  
  95.     }  
  96.   
  97.     /** 
  98.      * 如果锁空闲立即返回 获取失败 一直等待 
  99.      */  
  100.     @Override  
  101.     public void lock(String key) {  
  102.         Jedis jedis = null;  
  103.         try {  
  104.             jedis = getResource();  
  105.             do {  
  106.                 LOGGER.debug("lock key: " + key);  
  107.                 Long i = jedis.setnx(key, key);  
  108.                 if (i == 1) {  
  109.                     jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
  110.                     LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
  111.                     return;  
  112.                 } else {  
  113.                     if (LOGGER.isDebugEnabled()) {  
  114.                         String desc = jedis.get(key);  
  115.                         LOGGER.debug("key: " + key + " locked by another business:" + desc);  
  116.                     }  
  117.                 }  
  118.                 Thread.sleep(300);  
  119.             } while (true);  
  120.         } catch (JedisConnectionException je) {  
  121.             LOGGER.error(je.getMessage(), je);  
  122.             returnBrokenResource(jedis);  
  123.         } catch (Exception e) {  
  124.             LOGGER.error(e.getMessage(), e);  
  125.         } finally {  
  126.             returnResource(jedis);  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 释放锁 
  132.      */  
  133.     @Override  
  134.     public void unLock(String key) {  
  135.         List<String> list = new ArrayList<String>();  
  136.         list.add(key);  
  137.         unLock(list);  
  138.     }  
  139.   
  140.     /** 
  141.      * 批量获取锁 如果全部获取 立即返回true, 部分获取失败 返回false 
  142.      *  
  143.      * @return 
  144.      */  
  145.     @Override  
  146.     public boolean tryLock(List<String> keyList) {  
  147.         return tryLock(keyList, 0L, null);  
  148.     }  
  149.   
  150.     /** 
  151.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
  152.      *  
  153.      * @param timeout 
  154.      * @param unit 
  155.      * @return 
  156.      */  
  157.     @Override  
  158.     public boolean tryLock(List<String> keyList, long timeout, TimeUnit unit) {  
  159.         Jedis jedis = null;  
  160.         try {  
  161.             //需要的锁  
  162.             List<String> needLocking = new CopyOnWriteArrayList<String>();  
  163.             //得到的锁  
  164.             List<String> locked = new CopyOnWriteArrayList<String>();  
  165.             jedis = getResource();  
  166.             long nano = System.nanoTime();  
  167.             do {  
  168.                 // 构建pipeline,批量提交  
  169.                 Pipeline pipeline = jedis.pipelined();  
  170.                 for (String key : keyList) {  
  171.                     needLocking.add(key);  
  172.                     pipeline.setnx(key, key);  
  173.                 }  
  174.                 LOGGER.debug("try lock keys: " + needLocking);  
  175.                 // 提交redis执行计数,批量处理完成返回  
  176.                 List<Object> results = pipeline.syncAndReturnAll();  
  177.                 for (int i = 0; i < results.size(); ++i) {  
  178.                     Long result = (Long) results.get(i);  
  179.                     String key = needLocking.get(i);  
  180.                     if (result == 1) { // setnx成功,获得锁  
  181.                         jedis.expire(key, DEFAULT_BATCH_EXPIRE_TIME);  
  182.                         locked.add(key);  
  183.                     }  
  184.                 }  
  185.                 needLocking.removeAll(locked); // 已锁定资源去除  
  186.   
  187.                 if (needLocking.size() == 0) { //成功获取全部的锁  
  188.                     return true;  
  189.                 } else {  
  190.                     // 部分资源未能锁住  
  191.                     LOGGER.debug("keys: " + needLocking + " locked by another business:");  
  192.                 }  
  193.   
  194.                 if (timeout == 0) {  
  195.                     break;  
  196.                 }  
  197.                 Thread.sleep(500);  
  198.             } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
  199.   
  200.             // 得不到锁,释放锁定的部分对象,并返回失败  
  201.             if (locked.size() > 0) {  
  202.                 jedis.del(locked.toArray(new String[0]));  
  203.             }  
  204.             return false;  
  205.         } catch (JedisConnectionException je) {  
  206.             LOGGER.error(je.getMessage(), je);  
  207.             returnBrokenResource(jedis);  
  208.         } catch (Exception e) {  
  209.             LOGGER.error(e.getMessage(), e);  
  210.         } finally {  
  211.             returnResource(jedis);  
  212.         }  
  213.         return true;  
  214.     }  
  215.   
  216.     /** 
  217.      * 批量释放锁 
  218.      */  
  219.     @Override  
  220.     public void unLock(List<String> keyList) {  
  221.         List<String> keys = new CopyOnWriteArrayList<String>();  
  222.         for (String key : keyList) {  
  223.             keys.add(key);  
  224.         }  
  225.         Jedis jedis = null;  
  226.         try {  
  227.             jedis = getResource();  
  228.             jedis.del(keys.toArray(new String[0]));  
  229.             LOGGER.debug("release lock, keys :" + keys);  
  230.         } catch (JedisConnectionException je) {  
  231.             LOGGER.error(je.getMessage(), je);  
  232.             returnBrokenResource(jedis);  
  233.         } catch (Exception e) {  
  234.             LOGGER.error(e.getMessage(), e);  
  235.         } finally {  
  236.             returnResource(jedis);  
  237.         }  
  238.     }  
  239.   
  240.     /** 
  241.      * 获取redis客户端 
  242.      * @return 
  243.      */  
  244.     private Jedis getResource() {  
  245.         return jedisPool.getResource();  
  246.     }  
  247.   
  248.     /** 
  249.      * 销毁连接 
  250.      * @param jedis 
  251.      */  
  252.     private void returnBrokenResource(Jedis jedis) {  
  253.         if (jedis == null) {  
  254.             return;  
  255.         }  
  256.         try {  
  257.             //中断链接  
  258.             jedisPool.returnBrokenResource(jedis);  
  259.         } catch (Exception e) {  
  260.             LOGGER.error(e.getMessage(), e);  
  261.         }  
  262.     }  
  263.   
  264.     /** 
  265.      * 重新初始化对象 
  266.      * @param jedis 
  267.      */  
  268.     private void returnResource(Jedis jedis) {  
  269.         if (jedis == null) {  
  270.             return;  
  271.         }  
  272.         try {  
  273.             jedisPool.returnResource(jedis);  
  274.         } catch (Exception e) {  
  275.             LOGGER.error(e.getMessage(), e);  
  276.         }  
  277.     }  
  278. }  
3.分布式锁测试
  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5. import redis.clients.jedis.JedisPool;  
  6. import redis.clients.jedis.JedisPoolConfig;  
  7.   
  8. public class RedisLockMain {  
  9.       
  10.     public static void main(String[] args) {  
  11.         //创建jedis池配置实例    
  12.         JedisPoolConfig config = new JedisPoolConfig();     
  13.         //设置池配置项值    
  14.         config.setMaxTotal(1024);      
  15.         config.setMaxIdle(200);      
  16.         config.setMaxWaitMillis(1000);      
  17.         config.setTestOnBorrow(true);      
  18.         config.setTestOnReturn(true);   
  19.           
  20.         //根据配置实例化jedis池    
  21.         JedisPool  pool = new JedisPool(config,"192.168.100.205"6379);    
  22.         IRedisLockHandler lock = new RedisLockHandler(pool);  
  23.         if(lock.tryLock("abcd",20,TimeUnit.SECONDS)){  
  24.             System.out.println(" get lock ...");  
  25.         }else{  
  26.             System.out.println(" not get lock ...");  
  27.         }  
  28.        lock.unLock("abcd");  
  29.   
  30.     }  
  31.   
  32. }  
关于通过spring aop实现分布式锁,请参考:http://blog.csdn.net/michaelzhaozero/article/details/23746059
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis 分布式锁实现 的相关文章

  • 策略模式还是命令模式?

    假设我有一个金融交易列表 我需要针对这些交易执行一系列验证规则 一个例子是我有一笔购买产品的交易 但是首先我需要验证交易中的帐户是否有足够的可用资金 产品没有售完等 由于这些规则 交易将是标记为拒绝 并应指定错误代码 当然 我正在考虑用一个
  • 重写 getPreferredSize() 会破坏 LSP

    我总是在这个压倒一切的网站上看到建议getPreferredSize 而不是使用setPreferredSize 例如 如前面的线程所示 对于固定大小的组件 使用重写 getPreferredSize 而不是使用 setPreferredS
  • JUnit5 平台启动器 API - 如果没有至少一个测试引擎,则无法创建启动器

    我正在尝试升级我们的自动化测试套件的测试能力以接受 JUnit5 测试并遵循JUnit 平台启动器 API 说明 https junit org junit5 docs current user guide launcher api我收到错
  • 在java中将字符串日期转换为美国格式

    我有下面的代码 其中日期为字符串类型 我必须将其设置为美国格式 所以下面我已经展示了它 private static final SimpleDateFormat usOutputDate new SimpleDateFormat MM d
  • Java:SortedMap、TreeMap、可比较?如何使用?

    我有一个对象列表 需要根据其中一个字段的属性进行排序 我听说 SortedMap 和 Comparator 是实现此目的的最佳方法 我是否要与正在排序的类实现 Comparable 还是创建一个新类 如何实例化 SortedMap 并传入
  • Struts 1 到 Spring 迁移 - 策略

    我有一个legacy银行应用程序编码为Struts 1 JSP现在的要求是迁移后端 目前为 MVC to Springboot MVC 后续UI JSP 将迁移到angular Caveats 1 后端不是无状态的 2 会话对象中存储了大量
  • 如何在 JPA 和 Hibernate 中将数据库生成的列值定义为只读字段?

    使用 MariaDB 10 2 可以定义日期时间的默认值 例如创建和最后修改 我应该如何将此列作为只读字段访问 因为这个值应该只在数据库的控制之下 并且不应该从代码中修改 但我想在代码中读取这个属性 这很简单 只需设置insertable
  • 纱线上的火花,连接到资源管理器 /0.0.0.0:8032

    我正在我的开发机器 Mac 上编写 Spark 程序 hadoop的版本是2 6 spark的版本是1 6 2 hadoop集群有3个节点 当然都在linux机器上 我在idea IDE中以spark独立模式运行spark程序 它运行成功
  • 生成 equals 和 hashcode 时忽略属性

    假设我有一个类 Customer public class Customer private String firstName private String lastName private String doNotAddMeToEqual
  • 在 Java 中创建 XML 文件的最佳方法是什么?

    我们目前使用 dom4j 来创建 XML 文件 不过 我猜现在有更好的东西了 如果我们使用的是 Java 1 6 或更高版本 那么在编写 XML 文件时最好使用什么类 运行速度最快 使用简单 我不需要构建一个 DOM 然后编写整个 DOM
  • 在 java 中运行外部应用程序但不要等待它完成

    我正在用java编写一个应用程序 允许我运行其他应用程序 为此 我使用了 Process 类对象 但当我这样做时 应用程序会等待进程结束 然后再退出 有没有办法在 Java 中运行外部应用程序 但不等待它完成 public static v
  • 嵌套字段的 Comparator.comparing(...)

    假设我有一个这样的域模型 class Lecture Course course getters class Course Teacher teacher int studentSize getters class Teacher int
  • 如何从字符串中解析一个大整数? [复制]

    这个问题在这里已经有答案了 我有一个这样的方法 Integer parseInt myInt 不是这个整数变得很长 我得到以下异常 java lang NumberFormatException For input string 40001
  • 如何在不反编译的情况下更改已编译的.class文件?

    我想更改 class 文件方法 我安装 JD Eclipse Decompiler 并打开 class 文件 我添加了一些代码并保存 class 文件 但是 class 文件没有改变 我不知道如何使用反编译器 如果可能的话 如何在不使用反编
  • 如何从intellij项目视图中隐藏不必要的文件?

    给定一个示例 gradle 项目 其项目结构如下所示 正如你所看到的 有很多东西你实际上不需要在想法中看到 但你需要它们存在 我知道下面被忽略的文件 文件夹类型Editor File Types但这些正在影响库和项目 idea 会在各处忽略
  • 当底层连接是有状态时如何使用 Apache HttpClient?

    我在谷歌上搜索了很多关于如何使用 HttpClient 进行多线程处理的信息 他们中的大多数人建议使用 ThreadSafeClientConnManager 但我的应用程序必须登录某个主机 登录表单页面 以便 HttpClient 获得底
  • 如何从 JavaFX 中的另一个控制器类访问 UI 元素?

    我有一个使用 NetBeans 8 编写的 JavaFX Java 8 应用程序 没有SceneBuilder 我的应用程序有一个主窗口 该窗口有自己的 FXML 文件 primary fxml 和自己的控制器类 FXMLPrimaryCo
  • Collections.sort(list) 和 list.sort(Comparator) 之间的区别

    有什么理由让我应该选择Collections sort list 方法而不是简单地调用list sort 内部Collections sort只是调用sort的方法List无论如何 上课 令人惊讶的是几乎每个人都告诉我使用Collectio
  • 决策树和规则引擎 (Drools)

    In the application that I m working on right now I need to periodically check eligibility of tens of thousands of object
  • java中void的作用是什么?

    返回类型 方法返回值的数据类型 如果方法不返回值 则返回 void http download oracle com javase tutorial java javaOO methods html http download oracle

随机推荐

  • 基于Matlab分析的电力系统可视化研究

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码及数据 1 概述 电力系统可视化研究是电力系统分析中一项具
  • IKE协议与实现

    一 IKE的作用 当应用环境的规模较小时 可以用手工配置SA 当应用环境规模较大 参与的节点位置不固定时 IKE可自动地为参与通信的实体协商SA 并对安全关联库 SAD 维护 保障通信安全 二 IKE的机制 IKE属于一种混合型协议 由In
  • php curl ajax get请求,PHP的curl的get,post请求-Fun言

    GET请求如下 param string url return mixed public function doGet url 初始化 ch curl init curl setopt ch CURLOPT URL url 执行后不直接打印
  • 面试题:从用户在浏览器输入域名,到浏览器显示出页面,这中间发生了什么(工作过程)?

    这是一道很基础的题 但是也容易被忽视 主要是要进行域名解析 1 在浏览器中输入地址 如 www baidu com 2 向DNS服务器查询网站IP地址 3 DNS服务器返回网站IP地址 如 119 75 217 56 4 浏览器得到IP地址
  • 使用pipeline加速Redis

    面试官 怎么快速删除10万个key 某厂面试题 prod环境 如何快速删除10万个key 带着思考 我们一来研究Redis pipeline why pipeline Redis客户端与server的请求 响应模型 前面的文章 Redis底
  • C#开发WinForm之DataGridView开发

    C 开发WinForm之DataGridView开发 原文 https blog csdn net achenyuan article details 84632751 文章目录 C 开发WinForm之DataGridView开发 基本的
  • 在Linux中使用selenium(环境部署)

    在Linux中使用selenium 环境部署 1 安装chrome 用下面的命令安装Google Chrome yum install https dl google com linux direct google chrome stabl
  • 【单片机笔记】K型热电偶单运放放大,单片机ADC采集电路

    以下内容来自百科 K型热电偶作为一种温度传感器 K型热电偶通常和显示仪表 记录仪表和电子调节器配套使用 K型热电偶可以直接测量各种生产中从0 到1300 范围的液体蒸汽和气体介质以及固体的表面温度 高清K型热电偶图片 K型热电偶是目前用量最
  • 【RuoYi-Vue-Plus】学习笔记 09 - 数据权限调用流程分析(参照 Mybatis Plus 数据权限插件)

    文章目录 前言 参考目录 代码分析 1 数据权限配置 MybatisPlusConfig 2 数据权限拦截器 PlusDataPermissionInterceptor 3 数据权限处理器 PlusDataPermissionHandler
  • 20-Docker-常用命令详解-docker attach

    常用命令详解 docker attach 前言 docker attach 语法格式 options 说明 使用示例 进入容器 和docker exec 的区别 前言 本篇来学习docker attach命令 docker attach 作
  • 解决 -bash: ifconfig: command not found 实测有效

    1 查看是否已经联网 输入ip addr 或 ip a 发现ens33 中不包含IP内容 2 修改配置步骤 1 输入 cd etc sysconfig network scripts 回车 找到ifcfg ens33 注意 cd后面有空格
  • 坐标系和投影 知识的内容介绍

    回想一下 接触遥感专业也有几个年头了 而现在越来越偏离遥感了 突然想着把自己脑中的遥感知识整理出来 首先想到的便是坐标系和投影 我想这个东西困扰着80 以上的测绘 遥感和GIS领域的从业人员吧 群里经常有人问 我自己曾经也很迷糊 什么大地坐
  • Java测试题_1

    单选题 1 class Base Base System out print Base public class Alpha extends Base public static void main String args new Alph
  • JavaScript 获取 input 输入框内容的方法

    在 JavaScript 中获取 input 输入框内容的方法有以下几种 使用 document getElementById 方法获取输入框元素 再通过 value 属性获取输入框内容 示例代码如下 var input document
  • 吴恩达老师深度学习视频课笔记:逻辑回归公式推导及C++实现

    逻辑回归 Logistic Regression 是一个二分分类算法 逻辑回归的目标是最小化其预测与训练数据之间的误差 为了训练逻辑回归模型中的参数w和b 需要定义一个成本函数 cost function 成本函数 cost functio
  • Golang匿名结构体的使用

    一 结构体基础 结构体 struct 将多个不同类型的字段集中组成一种复合类型 按声明时的字段顺序初始化 type user struct name string age byte user user Tom 2 定义匿名结构体时没有 ty
  • 编程TRICK

    一 20200729 1 image和annots的数据类型要统一 如image annots设为np float32 在具体函数中 输入和输出的数据类型要保持一致 中间具体应用再改变数据类型 2 仿射变换可以用PIL的transform
  • Flowable入门系列文章113 - 进程实例 02

    1 激活或暂停流程实例 PUT运行时 process instances processInstanceId 表1 激活或暂停流程实例 URL参数 参数 需要 值 描述 processInstanceId 是 串 激活 挂起的流程实例的ID
  • 4.bio、request和request_queue

    通常一个bio对应上层传递给块层的I O请求 每个bio结构体实例及其包含的bvec iter bio vec结构体实例描述了该I O请求的开始扇区 数据方向 读还是写 数据放入的页 其定义如代码清单13 3所示 struct bvec i
  • Redis 分布式锁实现

    原文地址 http blog csdn net zhu tianwei article details 44927331 Redis是一个key value存储系统 和Memcached类似 但是解决了断电后数据完全丢失的情况 而且她支持更