案例一:实现对共享数据的并发操作

案例一:实现对共享数据的并发操作

项目需求:在学生管理系统中,学生是可以实现在线选课的,课程的名额是一个共享资源,在并发操作下,如果不做任何的安全操作的话,就会很危险。

我的处理方式:
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("出现异常");
        }
    }
}






全部评论

相关推荐

牛舌:如果我不想去,不管对方给了多少,我一般都会说你们给得太低了。这样他们就会给下一个offer的人更高的薪资了。
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务