分布式锁的实现

本文深入探讨Redis实现分布式锁的多种方式,包括increment、setNx及Redission框架的使用,对比不同方法的优缺点,如increment方式的简单实现与可能的死锁问题,setNx方式的锁抢占失败情况,以及Redission提供的高级锁管理功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现方式

  1. redis的increment方式抢占锁成功返回1失败返回0
  2. setNx方式抢占锁
  3. redission框架的使用

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>redissionLock</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.8.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

application.properties文件

server.port=9090
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=2
spring.redis.password=
spring.redis.timeout=1s
spring.redis.lettuce.pool.min-idle=1
spring.redis.lettuce.pool.max-active=500

TestController.java addLock()采用increment方式

@RestController
public class TestController {
	@Resource
	RedisTemplate<String, String> redisTemplate;
	String redisKey = "basedata:productKey";
	String number = "basedata:number";

	@RequestMapping("/addProduct")
	public void addProduct() throws RuntimeException {
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		redisTemplate.opsForValue().set(number, 10 + "");
	}

	@RequestMapping("/addLock")
	public void addLock() throws RuntimeException {
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		try {
			long num = redisTemplate.opsForValue().increment(redisKey, 1);
			redisTemplate.expire(redisKey, 2, TimeUnit.SECONDS);//防止死锁
			if (num == 1) {
				int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
				if (stack > 1) {
					redisTemplate.opsForValue().set(number, (stack - 1) + "");
					System.out.println("成功" + (stack - 1));
				} else {
					System.out.println("失败");
				}
			}
		} finally {
			redisTemplate.opsForValue().increment(redisKey, -1);
		}
	}
}

注:redisTemplate.expire(redisKey, 2, TimeUnit.SECONDS);//防止死锁其实还是会有bug;当前节点执行超时,另一个节点获取了锁,而当节点恰好执行了finall的释放锁,导致其他节点也获取了锁,数据错误,因此需要不停更新超时时间

public class ThreadWorker extends Thread {
	private String redisKey;
	private boolean stopflag;
	RedisTemplate<String, String> redisTemplate;
	public ThreadWorker(String redisKey ,RedisTemplate<String, String> redisTemplate) {
		this.redisKey = redisKey;
		this.redisTemplate=redisTemplate;
	}
	 @Override
	public void run() {
	 while(stopflag!=false) {
			Boolean expire = redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
			if(!expire) {
				break;
			}
		 try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				System.out.println(e);
			}
	 }
	}
	public String getRedisKey() {
		return redisKey;
	}
	public void setRedisKey(String redisKey) {
		this.redisKey = redisKey;
	}
	public boolean isStopflag() {
		return stopflag;
	}
	public void setStopflag(boolean stopflag) {
		this.stopflag = stopflag;
	}
}

setNx方式抢占锁,会发生锁抢占失败

public void addSetNexLock1() throws RuntimeException {
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		ThreadWorker worker = null;
		Boolean setNX = getLock(redisTemplate);
		try {
			worker = new ThreadWorker(redisKey, redisTemplate);
			worker.start();
			if (setNX) {
				int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
				if (stack > 1) {
					redisTemplate.opsForValue().set(number, (stack - 1) + "");
					System.out.println("成功" + (stack - 1));
				} else {
					System.out.println("失败");
				}
			} else {
				list.add("1");
			}
		} finally {
			if (setNX) {
				worker.setStopflag(true);
				redisTemplate.delete(redisKey);
			}
		}
	}

自旋抢锁方式

	private Boolean getLock(RedisTemplate<String, String> redisTemplate) {
		RedisConnectionFactory ConnectionFactory= redisTemplate.getConnectionFactory();
		Boolean setNX = ConnectionFactory.getConnection().setNX(redisKey.getBytes(), "1".getBytes());
		while (true) {
			if (!setNX) {
				setNX = setIfAbsent.getConnection().setNX(redisKey.getBytes(), "1".getBytes());
			} else {
				break;
			}
		}
		return setNX;
	}

Redission方式

加入依赖

    	<dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>3.10.6</version>
		</dependency>
@RequestMapping("/addRedissionLock")
	public void addRedissionLock() throws RuntimeException, InterruptedException {
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		/*
		 * HashOperations<String, Object, Object> opsForHash =
		 * redisTemplate.opsForHash(); opsForHash.putIfAbsent(redisKey, hashKey, 1);
		 */
		// 创建锁对象,并制定锁的名称
		RLock tasklock = redissonClient.getLock("taskLock");
		// 获取锁,设置自动失效时间为50s
		boolean isLock = tasklock.tryLock(50, TimeUnit.SECONDS);
		try {
			if (isLock) {
				int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
				if (stack > 1) {
					redisTemplate.opsForValue().set(number, (stack - 1) + "");
					System.out.println("成功" + (stack - 1));
				} else {
					System.out.println("失败");
				}
			}
		} finally {
			tasklock.unlock();
		}
	}

介绍一款测压工具Apache JMeter
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值