案例一:实现对共享数据的并发操作
案例一:实现对共享数据的并发操作
项目需求:在学生管理系统中,学生是可以实现在线选课的,课程的名额是一个共享资源,在并发操作下,如果不做任何的安全操作的话,就会很危险。
我的处理方式:
1、使用Synchronized
2、使用ReenTrackLock
3、使用Volatile关键字(属于强行使用了)
4、使用RabbitMQ消息队列实现并发操作
个人文档如下:
测试对共享资源并发竞争
需求:1000个学生对40个课程名额进行争抢,实现在高并发的情况下,实现压测。
方式一:使用非安全的方式实现:
前端设计:<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>争抢课程</title>
</head>
<body>
<!--<a th:href="@{/student/getCourse/{i}(i = 1)}">争抢课程</a>-->
<script th:src="@{/jquery-1.9.1.js}"></script>
<button onclick="ceshi(1000)">争抢课程</button>
</body>
<script type="text/javascript">
function ceshi(number) {
for (var i = 1; i <= number; i++) {
$.get("/student/getCourse/" + i,function (result) {
})
}
}
</script>
</html>
Controller层:
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
/**
* 获得课程剩余数量
* 现在的代码就是有问题的,可能会出现线程安全问题的
* @return
*/
@RequestMapping("/getCourse/{i}")
@ResponseBody
public String getCourse(@PathVariable String i) {
System.out.println("当前进入的消费者ID:" + i);
// 获取课程剩余数量
int courseCount = studentService.getCourseCountService();
// 判断课程数量的大小
if (courseCount > 0) {
// 课程数量减一
int decreaseCourse = studentService.decreaseCourseCountService();
if (decreaseCourse == 1) {
System.out.println("消费者ID" + i + " 争夺成功!");
}
}
return null;
}
}
Service层:
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 获得课程剩余数量
* @return
*/
@Override
public int getCourseCountService() {
return studentMapper.getCourseCountMapper();
}
/**
* 课程剩余数量减一
* @return
*/
@Override
public int decreaseCourseCountService() {
return studentMapper.decreaseCourseCountMapper();
}
}
Mapper层:不做具体的展示。
方式二:使用Synchronized关键字实现同步机制
全部代码相同,只需要添加synchronized关键字在controller方法上或者service实现类方法上。
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
/**
* 获得课程剩余数量
* 现在的代码就是有问题的,可能会出现线程安全问题的
* @return
*/
@RequestMapping("/getCourse/{i}")
@ResponseBody
public synchronized String getCourse(@PathVariable String i) {
System.out.println("当前进入的消费者ID:" + i);
// 获取课程剩余数量
int courseCount = studentService.getCourseCountService();
// 判断课程数量的大小
if (courseCount > 0) {
// 课程数量减一
int decreaseCourse = studentService.decreaseCourseCountService();
if (decreaseCourse == 1) {
System.out.println("消费者ID" + i + " 争夺成功!");
}
}
return null;
}
}
方式三:使用Volatile关键字实现(使用它的线程可见的特点实现)
方式四:使用ReennTractLock锁实现数据的同步
使用对象锁之前是需要实例化一个对象锁。
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
/**
* 实例化一个ReenTrackLock锁对象
*/
private ReentrantLock reentrantLock = new ReentrantLock();
/**
* 获得课程剩余数量
* 现在的代码就是有问题的,可能会出现线程安全问题的
* @return
*/
@RequestMapping("/getCourse/{i}")
@ResponseBody
public String getCourse(@PathVariable String i) {
try {
// 实现加锁
reentrantLock.lock();
System.out.println("当前进入的消费者ID:" + i);
// 获取课程剩余数量
int courseCount = studentService.getCourseCountService();
// 判断课程数量的大小
if (courseCount > 0) {
// 课程数量减一
int decreaseCourse = studentService.decreaseCourseCountService();
if (decreaseCourse == 1) {
System.out.println("消费者ID" + i + " 争夺成功!");
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("出现异常");
} finally {
// 实现放锁
reentrantLock.unlock();
}
return null;
}
}
方法五:使用RabbitMQ消息队列实现数据的同步机制
在使用RabbitMQ的技术框架中,想要使用消息队列实现并发消息的处理,是完全可以实现的。在架构设计时,根据高内聚的设计原则,将消息的发送方Producer和消息的接收方Consumer分别放在不同的模块中,形成两个模块:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
rabbitmq_producer模块
RabbitMQ配置类:@Configuration
public class RabbitConfig {
private final String EXCHANGE_NAME = "COURSE_EXCHANGE"; // 交换器的名称
private final String QUEUE_NAME = "COURSE_QUEUE"; // 队列的名称
// 配置交换器
@Bean("EXCHANGE_NAME")
public Exchange getExchange() {
return new DirectExchange(EXCHANGE_NAME);
}
// 配置队列
@Bean("QUEUE_NAME")
public Queue getQueue() {
return new Queue(QUEUE_NAME);
}
// 绑定队列到交换器上
@Bean
public Binding binding(@Qualifier("EXCHANGE_NAME") Exchange exchange, @Qualifier("QUEUE_NAME") Queue queue) {
return BindingBuilder
.bind(queue)
.to(exchange)
.with("course")
.noargs();
}
}
Controller层接口:
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("/getCourse/{i}")
@ResponseBody
public String getCourse(@PathVariable String i) {
rabbitTemplate.convertAndSend("COURSE_EXCHANGE", "course", i);
return null;
}
}
rabbitmq_consumer模块
Controller层:@Component
public class StudentRabbitConsumer {
@Autowired
private StudentService studentService;
/**
* 实现消息的监听
* 对队列中的每一个消息都进行接收和处理。
* @param message
*/
@RabbitListener(queues = "COURSE_QUEUE")
public void ListenerConsumer(String message) {
try {
System.out.println("当前进入的消费者ID:" + message);
// 获取课程剩余数量
int courseCount = studentService.getCourseCountService();
// 判断课程数量的大小
if (courseCount > 0) {
// 课程数量减一
int decreaseCourse = studentService.decreaseCourseCountService();
if (decreaseCourse == 1) {
System.out.println("消费者ID" + message + " 争夺成功!");
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("出现异常");
}
}
}
Serviec层:
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 获得课程剩余数量
*
* @return
*/
@Override
public int getCourseCountService() {
return studentMapper.getCourseCountMapper();
}
/**
* 课程剩余数量减一
*
* @return
*/
@Override
public int decreaseCourseCountService() {
return studentMapper.decreaseCourseCountMapper();
}
}
@Component
public class StudentRabbitConsumer {
@Autowired
private StudentService studentService;
//记录课程剩余的数量
private static Integer COURSE_NUMBER;
//函数执行的次数
private static Integer Integer_Flag = 1;
/**
* 实现消息的监听
* 对队列中的每一个消息都进行接收和处理。
* @param message
*/
@RabbitListener(queues = "COURSE_QUEUE")
public void ListenerConsumer(String message) {
if (Integer_Flag == 1) {
COURSE_NUMBER = studentService.getCourseCountService();
Integer_Flag = 0;
}
try {
System.out.println("当前进入的消费者ID:" + message);
// 判断课程数量的大小
if (COURSE_NUMBER > 0) {
// 课程数量减一
COURSE_NUMBER--;
System.out.println("消费者ID" + message + " 争夺成功!");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("出现异常");
}
}
}

查看5道真题和解析