redis锁操作
模拟多线程触发
package com.ws.controller; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock; @Api(value = "锁机制", description = "锁机制说明")
@RestController
public class LockController {
private static long count = 20;//黄牛
private CountDownLatch countDownLatch = new CountDownLatch(5); @Resource(name="redisLock")
private Lock lock; @ApiOperation(value="售票")
@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();
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) + "张票");
}
}
}
redis锁实现
package com.ws.lock; import com.enjoy.utils.FileUtils;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis; import javax.annotation.Resource;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; @Service
public class RedisLock implements Lock { private static final String KEY = "LOCK_KEY"; @Resource
private JedisConnectionFactory factory; private ThreadLocal<String> local = new ThreadLocal<>(); @Override
//阻塞式的加锁
public void lock() {
//1.尝试加锁
if(tryLock()){
return;
}
//2.加锁失败,当前任务休眠一段时间
try {
Thread.sleep(10);//性能浪费
} catch (InterruptedException e) {
e.printStackTrace();
}
//3.递归调用,再次去抢锁
lock();
} @Override
//阻塞式加锁,使用setNx命令返回OK的加锁成功,并生产随机值
public boolean tryLock() {
//产生随机值,标识本次锁编号
String uuid = UUID.randomUUID().toString();
Jedis jedis = (Jedis) factory.getConnection().getNativeConnection(); /**
* key:我们使用key来当锁
* uuid:唯一标识,这个锁是我加的,属于我
* NX:设入模式【SET_IF_NOT_EXIST】--仅当key不存在时,本语句的值才设入
* PX:给key加有效期
* 1000:有效时间为 1 秒
*/
String ret = jedis.set(KEY, uuid,"NX","PX",1000); //设值成功--抢到了锁
if("OK".equals(ret)){
local.set(uuid);//抢锁成功,把锁标识号记录入本线程--- Threadlocal
return true;
} //key值里面有了,我的uuid未能设入进去,抢锁失败
return false;
} //正确解锁方式
public void unlock() {
//读取lua脚本
String script = FileUtils.getScript("unlock.lua");
//获取redis的原始连接
Jedis jedis = (Jedis) factory.getConnection().getNativeConnection();
//通过原始连接连接redis执行lua脚本
jedis.eval(script, Arrays.asList(KEY), Arrays.asList(local.get()));
} //----------------------------------------------- @Override
public Condition newCondition() {
return null;
} @Override
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return false;
} @Override
public void lockInterruptibly() throws InterruptedException {
} }
文件读取工具类
package com.ws.utils; import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader; 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;
} public static void main(String[] args) {
String path = FileUtils.class.getClassLoader().getResource("unlock.lua").getPath();
String script = FileUtils.readFileByLines(path);
System.out.println(script);
}
}
redis配置类
package com.ws.config; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig; @Configuration
public class RedisConfig { @Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(10);
jedisPoolConfig.setMaxTotal(10000);
return jedisPoolConfig;
} @Bean
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("192.168.42.111");
jedisConnectionFactory.setPort(6379);
jedisConnectionFactory.setPassword("12345678");
jedisConnectionFactory.setUsePool(true);
jedisConnectionFactory.setPoolConfig(jedisPoolConfig); return jedisConnectionFactory;
}
}
lua脚本
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
redis锁操作的更多相关文章
- (实例篇)php 使用redis锁限制并发访问类示例
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- php 使用redis锁限制并发访问类
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- PHP Redis锁
一.什么是 Redis Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库 二.什么是 Redis 分布式锁 分布式锁其实可以理解为:控 ...
- PHP进阶与redis锁限制并发访问功能示例
<?php /** * Redis锁操作类 * Date: 2017-06-30 * Author: fdipzone * Ver: 1.0 * * Func: * public lock 获取 ...
- 【连载】redis库存操作,分布式锁的四种实现方式[三]--基于Redis watch机制实现分布式锁
一.redis的事务介绍 1. Redis保证一个事务中的所有命令要么都执行,要么都不执行.如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行.而一旦客户端发 ...
- php的redis 操作类,适用于单台或多台、多组redis服务器操作
redis 操作类,包括单台或多台.多组redis服务器操作,适用于业务复杂.高性能要求的 php web 应用. redis.php: <?php /* redis 操作类,适用于单台或多台. ...
- 解锁redis锁的正确姿势
解锁redis锁的正确姿势 redis是php的好朋友,在php写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为.这个时候我们就要用到锁.锁的方式有好几种,php不能在内存中用锁 ...
- redis锁机制介绍与实例
转自:https://m.jb51.net/article/154421.htm 今天小编就为大家分享一篇关于redis锁机制介绍与实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要 ...
- 多线程并发问题解决之redis锁
一 问题背景 我们做的是医疗信息化系统,在系统中一条患者信息对医院中当前科室中的所有诊断医生是可见的,当有一个诊断医生点击按钮处理该数据时,数据的状态发生了变化,其他的医生就不可以再处理此患者的数据了 ...
随机推荐
- SQL Server2017+SSIS+Python
1.安装SQL Server2017 https://jingyan.baidu.com/article/76a7e409077997fc3a6e1559.html (1)JRE 7报错 只能安装JR ...
- Qt之先用了再说系列-多线程方式2
继Qt之先用了再说系列-多线程方式2 本次说说在Qt里多线程使用第2种方式,也是qt官方比较推荐用的方式,相对与直接继承QThread 重写run方法中灵活了一些,如果第一次使用可能会转不湾来,没有直 ...
- C#LeetCode刷题之#56-合并区间(Merge Intervals)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3676 访问. 给出一个区间的集合,请合并所有重叠的区间. 输入: ...
- The Definitive Guide to Ruby's C API The Ruby C API Running Ruby in C Running C in Ruby
最近在研究如何在C/C++中 嵌入ruby脚本,很感谢找到了一篇文章,分享一下. The Definitive Guide to Ruby's C API
- 【Azure DevOps系列】什么是Azure DevOps
DevOps DevOps是一种重视"软件开发人员(Dev)"和"IT运维技术人员(Ops)"之间沟通合作的文化,它促进开发和运营团队之间的协作,以自动化和可重 ...
- OAuth2.0分布式系统环境搭建
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 介绍 OAuth(开放授权)是一 ...
- R 安装包的方法
install.packages(packageName) install.packages(path_to_file, repos = NULL, type="source") ...
- 如何校验内存数据的一致性,DynamicExpresso 算是帮上大忙了
一:背景 1. 讲故事 记的在上一家公司做全内存项目的时候,因为一些关键表会在程序 startup 的时候全量灌入到内存中,但随着时间的推移,内存和数据库的同步偶尔会出现数据差异的情况,伴随着就是运营 ...
- 多线程系列(二)之Thread类
在上一遍文章中讲到多线程基础,在此篇文章中我们来学习C#里面Thread类.Thread类是在.net framework1.0版本中推出的API.如果对线程的概念还不太清楚 的小伙伴请阅读我的上一遍 ...
- consul、eureka、nacos对比
consul.eureka.nacos对比 配置中心 eureka 不支持 consul 支持 但用起来偏麻烦,不太符合springBoot框架的命名风格,支持动态刷新 nacos 支持 用起来简单, ...