Spring Boot 分布式锁与并发控制的应用场景
作者:星辰徐哥
Spring Boot 分布式锁与并发控制
30.1 学习目标与重点提示
学习目标:掌握Spring Boot分布式锁与并发控制的核心概念与使用方法,包括分布式锁的定义与特点、并发控制的定义与特点、Spring Boot与分布式锁的集成、Spring Boot的实际应用场景,学会在实际开发中处理分布式锁与并发控制问题。
重点:分布式锁的定义与特点、并发控制的定义与特点、Spring Boot与分布式锁的集成、Spring Boot的实际应用场景。
30.2 分布式锁与并发控制概述
分布式锁与并发控制是Java开发中的重要组件,用于处理分布式系统中的并发访问问题。
30.2.1 分布式锁的定义
定义:分布式锁是一种用于在分布式系统中实现资源共享访问控制的机制,确保在同一时间只有一个进程或线程能够访问共享资源。
作用:
- 防止资源的并发修改。
- 确保数据的一致性。
- 提高系统的可靠性。
常见的分布式锁:
- Redis分布式锁:Redis是一种开源的内存数据库,支持分布式锁。
- Zookeeper分布式锁:Zookeeper是一种开源的分布式协调服务,支持分布式锁。
- Etcd分布式锁:Etcd是一种开源的分布式键值存储系统,支持分布式锁。
✅ 结论:分布式锁是一种用于在分布式系统中实现资源共享访问控制的机制,作用是防止资源的并发修改、确保数据的一致性、提高系统的可靠性。
30.2.2 并发控制的定义
定义:并发控制是指在多进程或多线程环境中,控制对共享资源的访问,防止数据不一致和并发冲突。
作用:
- 防止数据不一致。
- 防止并发冲突。
- 提高系统的性能。
常见的并发控制技术:
- 锁机制:包括悲观锁和乐观锁。
- 事务机制:包括ACID属性。
- 并发集合:包括ConcurrentHashMap和CopyOnWriteArrayList。
✅ 结论:并发控制是指在多进程或多线程环境中,控制对共享资源的访问,作用是防止数据不一致、防止并发冲突、提高系统的性能。
30.3 Spring Boot与分布式锁的集成
Spring Boot与分布式锁的集成是Java开发中的重要内容。
30.3.1 集成Redis分布式锁的步骤
定义:集成Redis分布式锁的步骤是指使用Spring Boot与Redis分布式锁集成的方法。
步骤:
- 创建Spring Boot项目。
- 添加所需的依赖。
- 配置Redis。
- 创建分布式锁工具类。
- 创建业务层。
- 创建控制器类。
- 测试应用。
示例:
pom.xml文件中的依赖:
<dependencies>
<!-- Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>application.properties文件中的配置:
# 服务器端口 server.port=8080 # Redis连接信息 spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= spring.redis.database=0
分布式锁工具类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final String LOCK_KEY = "product-lock";
private static final long LOCK_EXPIRE_TIME = 30; // 锁的过期时间,单位秒
public boolean tryLock(String lockValue) {
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(LOCK_KEY, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
public void releaseLock(String lockValue) {
String value = stringRedisTemplate.opsForValue().get(LOCK_KEY);
if (lockValue.equals(value)) {
stringRedisTemplate.delete(LOCK_KEY);
}
}
}实体类:
public class Product {
private Long id;
private String productId;
private String productName;
private double price;
private int stock;
public Product() {
}
public Product(Long id, String productId, String productName, double price, int stock) {
this.id = id;
this.productId = productId;
this.productName = productName;
this.price = price;
this.stock = stock;
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", stock=" + stock +
'}';
}
}Repository接口:
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Repository
public class ProductRepository {
private List<Product> products = new ArrayList<>();
public ProductRepository() {
products.add(new Product(1L, "P001", "手机", 1000.0, 100));
products.add(new Product(2L, "P002", "电脑", 5000.0, 50));
products.add(new Product(3L, "P003", "电视", 3000.0, 80));
products.add(new Product(4L, "P004", "手表", 500.0, 200));
products.add(new Product(5L, "P005", "耳机", 300.0, 150));
}
public List<Product> getAllProducts() {
return products;
}
public Product getProductById(Long id) {
return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);
}
public void addProduct(Product product) {
product.setId((long) (products.size() + 1));
products.add(product);
}
public void updateProduct(Product product) {
Product existingProduct = getProductById(product.getId());
if (existingProduct != null) {
existingProduct.setProductId(product.getProductId());
existingProduct.setProductName(product.getProductName());
existingProduct.setPrice(product.getPrice());
existingProduct.setStock(product.getStock());
}
}
public void deleteProduct(Long id) {
products.removeIf(product -> product.getId().equals(id));
}
}Service类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private RedisLock redisLock;
public List<Product> getAllProducts() {
return productRepository.getAllProducts();
}
public Product getProductById(Long id) {
return productRepository.getProductById(id);
}
public void addProduct(Product product) {
productRepository.addProduct(product);
}
public void updateProduct(Product product) {
productRepository.updateProduct(product);
}
public void deleteProduct(Long id) {
productRepository.deleteProduct(id);
}
public boolean reduceStock(Long id, int quantity) {
String lockValue = UUID.randomUUID().toString();
try {
if (redisLock.tryLock(lockValue)) {
Product product = productRepository.getProductById(id);
if (product != null && product.getStock() >= quantity) {
product.setStock(product.getStock() - quantity);
productRepository.updateProduct(product);
return true;
}
return false;
}
return false;
} finally {
redisLock.releaseLock(lockValue);
}
}
}控制器类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/")
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping("/add")
public void addProduct(@RequestBody Product product) {
productService.addProduct(product);
}
@PutMapping("/edit/{id}")
public void editProduct(@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
productService.updateProduct(product);
}
@DeleteMapping("/delete/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
@PostMapping("/reduceStock/{id}/{quantity}")
public boolean reduceStock(@PathVariable Long id, @PathVariable int quantity) {
return productService.reduceStock(id, quantity);
}
}应用启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RedisLockApplication {
public static void main(String[] args) {
SpringApplication.run(RedisLockApplication.class, args);
}
}测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RedisLockApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void contextLoads() {
}
@Test
void testGetAllProducts() {
List products = restTemplate.getForObject("http://localhost:" + port + "/api/products/", List.class);
assertThat(products).hasSize(5);
}
@Test
void testReduceStock() {
boolean result = restTemplate.postForObject("http://localhost:" + port + "/api/products/reduceStock/1/10", null, Boolean.class);
assertThat(result).isTrue();
}
}✅ 结论:集成Redis分布式锁的步骤包括创建Spring Boot项目、添加所需的依赖、配置Redis、创建分布式锁工具类、创建业务层、创建控制器类、测试应用。
30.4 Spring Boot的实际应用场景
在实际开发中,Spring Boot分布式锁与并发控制的应用场景非常广泛,如:
- 实现产品库存的并发扣减。
- 实现用户账户的并发扣款。
- 实现订单的并发创建。
- 实现数据的并发更新。
示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
class ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private RedisLock redisLock;
public List<Product> getAllProducts() {
return productRepository.getAllProducts();
}
public Product getProductById(Long id) {
return productRepository.getProductById(id);
}
public void addProduct(Product product) {
productRepository.addProduct(product);
}
public void updateProduct(Product product) {
productRepository.updateProduct(product);
}
public void deleteProduct(Long id) {
productRepository.deleteProduct(id);
}
public boolean reduceStock(Long id, int quantity) {
String lockValue = UUID.randomUUID().toString();
try {
if (redisLock.tryLock(lockValue)) {
Product product = productRepository.getProductById(id);
if (product != null && product.getStock() >= quantity) {
product.setStock(product.getStock() - quantity);
productRepository.updateProduct(product);
return true;
}
return false;
}
return false;
} finally {
redisLock.releaseLock(lockValue);
}
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final String LOCK_KEY = "product-lock";
private static final long LOCK_EXPIRE_TIME = 30;
public boolean tryLock(String lockValue) {
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(LOCK_KEY, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
public void releaseLock(String lockValue) {
String value = stringRedisTemplate.opsForValue().get(LOCK_KEY);
if (lockValue.equals(value)) {
stringRedisTemplate.delete(LOCK_KEY);
}
}
}
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Repository
class ProductRepository {
private List<Product> products = new ArrayList<>();
public ProductRepository() {
products.add(new Product(1L, "P001", "手机", 1000.0, 100));
products.add(new Product(2L, "P002", "电脑", 5000.0, 50));
products.add(new Product(3L, "P003", "电视", 3000.0, 80));
products.add(new Product(4L, "P004", "手表", 500.0, 200));
products.add(new Product(5L, "P005", "耳机", 300.0, 150));
}
public List<Product> getAllProducts() {
return products;
}
public Product getProductById(Long id) {
return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);
}
public void addProduct(Product product) {
product.setId((long) (products.size() + 1));
products.add(product);
}
public void updateProduct(Product product) {
Product existingProduct = getProductById(product.getId());
if (existingProduct != null) {
existingProduct.setProductId(product.getProductId());
existingProduct.setProductName(product.getProductName());
existingProduct.setPrice(product.getPrice());
existingProduct.setStock(product.getStock());
}
}
public void deleteProduct(Long id) {
products.removeIf(product -> product.getId().equals(id));
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/")
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping("/add")
public void addProduct(@RequestBody Product product) {
productService.addProduct(product);
}
@PutMapping("/edit/{id}")
public void editProduct(@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
productService.updateProduct(product);
}
@DeleteMapping("/delete/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
@PostMapping("/reduceStock/{id}/{quantity}")
public boolean reduceStock(@PathVariable Long id, @PathVariable int quantity) {
return productService.reduceStock(id, quantity);
}
}
@SpringBootApplication
public class RedisLockApplication {
public static void main(String[] args) {
SpringApplication.run(RedisLockApplication.class, args);
}
}
// 测试类
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RedisLockApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void contextLoads() {
}
@Test
void testGetAllProducts() {
List products = restTemplate.getForObject("http://localhost:" + port + "/api/products/", List.class);
assertThat(products).hasSize(5);
}
@Test
void testReduceStock() {
boolean result = restTemplate.postForObject("http://localhost:" + port + "/api/products/reduceStock/1/10", null, Boolean.class);
assertThat(result).isTrue();
}
}输出结果:
- 访问http://localhost:8080/api/products/:返回所有产品信息。
- 访问http://localhost:8080/api/products/reduceStock/1/10:返回true,表示库存扣减成功。
✅ 结论:在实际开发中,Spring Boot分布式锁与并发控制的应用场景非常广泛,需要根据实际问题选择合适的分布式锁和并发控制方法。
总结
本章我们学习了Spring Boot分布式锁与并发控制,包括分布式锁的定义与特点、并发控制的定义与特点、Spring Boot与分布式锁的集成、Spring Boot的实际应用场景,学会了在实际开发中处理分布式锁与并发控制问题。其中,分布式锁的定义与特点、并发控制的定义与特点、Spring Boot与分布式锁的集成、Spring Boot的实际应用场景是本章的重点内容。从下一章开始,我们将学习Spring Boot的其他组件、微服务等内容。
到此这篇关于Spring Boot 分布式锁与并发控制的应用场景的文章就介绍到这了,更多相关Spring Boot 分布式锁与并发控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
