亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java并發(fā)編程service層處理并發(fā)事務(wù)加鎖可能會(huì)無(wú)效問(wèn)題

 更新時(shí)間:2023年07月27日 09:43:58   作者:煙雨樓臺(tái)笑江湖  
這篇文章主要介紹了Java并發(fā)編程service層處理并發(fā)事務(wù)加鎖可能會(huì)無(wú)效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

問(wèn)題描述

近期寫了一個(gè)單體架構(gòu)秒殺的功能,在對(duì)商品庫(kù)存進(jìn)行扣減,有線程安全問(wèn)題,因此加了Lock鎖進(jìn)行同步,但發(fā)現(xiàn)加鎖后并沒有控制住庫(kù)存線程安全的問(wèn)題,導(dǎo)致庫(kù)存仍被超發(fā)。

輸出一下代碼:

@Override
@Transactional(rollbackFor = Exception.class)
public Result startSeckillLock(long seckillId, long userId) {
	/**
	  * 這里加鎖,還是會(huì)出現(xiàn)超賣
      *
      * 因?yàn)檫M(jìn)入service方法中時(shí),spring事務(wù)已經(jīng)開啟,隔離級(jí)別默認(rèn)是可重復(fù)讀,
      * 因?yàn)槭聞?wù)先開啟,后加鎖,隔離級(jí)別為可重復(fù)讀的情況下,當(dāng)前線程讀不到其他線程更新的數(shù)據(jù),
      * 所以就會(huì)出現(xiàn)超賣的情況
      *
      * 下面方法通過(guò)aop加鎖,order = 1,在事務(wù)開啟之前加鎖
      *
      * 還有就是直接在controller中加鎖
      */
	lock.lock();
    try {
		//校驗(yàn)庫(kù)存
        String nativeSql = "SELECT number FROM seckill WHERE seckill_id = ?";
        Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
        Long number =  ((Number) object).longValue();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>> number : {}" + number);
        if(number > 0){
            //扣庫(kù)存
            nativeSql = "UPDATE seckill  SET number=? WHERE seckill_id = ?";
            dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{number - 1, seckillId});
            //創(chuàng)建訂單
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            killed.setCreateTime(new Timestamp(System.currentTimeMillis()));
            dynamicQuery.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
            //支付
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    } finally {
        lock.unlock();
    }
//        https://cloud.tencent.com/developer/article/1630866
//        finally 在 return 之后時(shí),先執(zhí)行 finally 后,再執(zhí)行該 return;
//        finally 內(nèi)含有 return 時(shí),直接執(zhí)行其 return 后結(jié)束;
//        finally 在 return 前,執(zhí)行完 finally 后再執(zhí)行 return。
//        return Result.ok(SeckillStatEnum.SUCCESS);
}

問(wèn)題分析

由于spring事務(wù)是通過(guò)AOP實(shí)現(xiàn)的,所以在startSeckillLock()方法執(zhí)行之前會(huì)開啟事務(wù),之后會(huì)有提交事務(wù)的邏輯。

而lock的動(dòng)作是發(fā)生在事務(wù)之內(nèi)。

數(shù)據(jù)庫(kù)默認(rèn)的事務(wù)隔離級(jí)別為可重復(fù)讀(repeatable-read)。

因?yàn)槭鞘聞?wù)先開啟后加鎖,隔離級(jí)別為可重復(fù)讀的情況下,當(dāng)前線程是讀取不到其他線程更新的數(shù)據(jù),也就是說(shuō)其他線程雖然更新了庫(kù)存且事務(wù)也提交了,但是因?yàn)楫?dāng)前線程已經(jīng)開啟了事務(wù)(可重復(fù)讀的隔離級(jí)別),所以當(dāng)前線程在事務(wù)中獲取到的仍然是開啟事務(wù)時(shí)的庫(kù)存,所以就會(huì)出現(xiàn)超賣的情況。

問(wèn)題解決

一:在controller層加鎖

二:在service層自己定義事務(wù)的開啟和提交,加鎖的代碼方到開啟事務(wù)之前,解鎖在提交事務(wù)之后

三:AOP+鎖

自定義注解ServiceLock:

@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface Servicelock { 
     String description()  default "";
}

自定義切面LockAspect:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Component
@Aspect
@Order(1)
public class LockAspect {
    private static final Lock lock = new ReentrantLock();
    @Pointcut("@annotation(com.wjy.seckill.common.aop.ServiceLock)")
    public void lockAspect() {
    }
    @Around("lockAspect()")
    public Object around(ProceedingJoinPoint joinPoint) {
        lock.lock();
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        return result;
    }
}

切入秒殺方法:

@Override
@ServiceLock
@Transactional(rollbackFor = Exception.class)
public Result startSeckillAopLock(long seckillId, long userId) {
	//校驗(yàn)庫(kù)存
    String nativeSql = "SELECT number FROM seckill WHERE seckill_id = ?";
    Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
    Long number =  ((Number) object).longValue();
    System.out.println(">>>>>>>>>>>>>>>>>>>>>> number : {}" + number);
    if(number > 0){
        //扣庫(kù)存
        nativeSql = "UPDATE seckill  SET number=? WHERE seckill_id = ?";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{number - 1, seckillId});
        //創(chuàng)建訂單
        SuccessKilled killed = new SuccessKilled();
        killed.setSeckillId(seckillId);
        killed.setUserId(userId);
        killed.setState((short)0);
        killed.setCreateTime(new Timestamp(System.currentTimeMillis()));
        dynamicQuery.save(killed);
        return Result.ok(SeckillStatEnum.SUCCESS);
        //支付
    }else{
    	return Result.error(SeckillStatEnum.END);
    }
}

至此問(wèn)題解決

表結(jié)構(gòu)

/*
 Navicat Premium Data Transfer
 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50732
 Source Host           : localhost:3306
 Source Schema         : spring-boot-seckill
 Target Server Type    : MySQL
 Target Server Version : 50732
 File Encoding         : 65001
 Date: 05/01/2022 15:51:06
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for seckill
-- ----------------------------
DROP TABLE IF EXISTS `seckill`;
CREATE TABLE `seckill`  (
  `seckill_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品庫(kù)存id',
  `name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名稱',
  `number` int(11) NOT NULL COMMENT '庫(kù)存數(shù)量',
  `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒殺開啟時(shí)間',
  `end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒殺結(jié)束時(shí)間',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `version` int(11) NOT NULL COMMENT '版本號(hào)',
  PRIMARY KEY (`seckill_id`) USING BTREE,
  INDEX `idx_start_time`(`start_time`) USING BTREE,
  INDEX `idx_end_time`(`end_time`) USING BTREE,
  INDEX `idx_create_time`(`create_time`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1004 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '秒殺庫(kù)存表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of seckill
-- ----------------------------
INSERT INTO `seckill` VALUES (1000, '1000元秒殺iphone8', 100, '2018-05-10 15:31:53', '2018-05-10 15:31:53', '2018-05-10 15:31:53', 0);
INSERT INTO `seckill` VALUES (1001, '500元秒殺ipad2', 100, '2018-05-10 15:31:53', '2018-05-10 15:31:53', '2018-05-10 15:31:53', 0);
INSERT INTO `seckill` VALUES (1002, '300元秒殺小米4', 100, '2018-05-10 15:31:53', '2018-05-10 15:31:53', '2018-05-10 15:31:53', 0);
INSERT INTO `seckill` VALUES (1003, '200元秒殺紅米note', 100, '2018-05-10 15:31:53', '2018-05-10 15:31:53', '2018-05-10 15:31:53', 0);
-- ----------------------------
-- Table structure for success_killed
-- ----------------------------
DROP TABLE IF EXISTS `success_killed`;
CREATE TABLE `success_killed`  (
  `seckill_id` bigint(20) NOT NULL COMMENT '秒殺商品id',
  `user_id` bigint(20) NOT NULL COMMENT '用戶Id',
  `state` tinyint(4) NOT NULL COMMENT '狀態(tài)標(biāo)示:-1指無(wú)效,0指成功,1指已付款',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  PRIMARY KEY (`seckill_id`, `user_id`) USING BTREE,
  INDEX `idx_create_time`(`create_time`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '秒殺成功明細(xì)表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of success_killed
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Graphics2D 寫圖片中文亂碼問(wèn)題及解決

    Graphics2D 寫圖片中文亂碼問(wèn)題及解決

    這篇文章主要介紹了Graphics2D 寫圖片中文亂碼問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java打亂數(shù)組元素簡(jiǎn)單代碼例子

    Java打亂數(shù)組元素簡(jiǎn)單代碼例子

    在Java編程中,我們經(jīng)常需要對(duì)數(shù)組進(jìn)行亂序操作(即將數(shù)組中的元素隨機(jī)打亂順序),這篇文章主要給大家介紹了關(guān)于Java打亂數(shù)組元素的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • java集合超詳細(xì)(最新推薦)

    java集合超詳細(xì)(最新推薦)

    在內(nèi)存中申請(qǐng)一塊空間用來(lái)存儲(chǔ)數(shù)據(jù),在Java中集合就是替換掉定長(zhǎng)的數(shù)組的一種引用數(shù)據(jù)類型,本文介紹java集合超詳細(xì)講解,感興趣的朋友一起看看吧
    2024-12-12
  • SpringBoot集成IJPay實(shí)現(xiàn)微信v3支付的示例代碼

    SpringBoot集成IJPay實(shí)現(xiàn)微信v3支付的示例代碼

    本文主要介紹了SpringBoot集成IJPay實(shí)現(xiàn)微信v3支付的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java程序與C語(yǔ)言的區(qū)別淺析

    Java程序與C語(yǔ)言的區(qū)別淺析

    Java和C語(yǔ)言雖有相同性,但兩者也有一定的不同。Java程序是面向?qū)ο蟮囊环N簡(jiǎn)單、分布式 、解釋、健壯、安全、結(jié)構(gòu)中立、可移植、高效能、多線程、動(dòng)態(tài)的語(yǔ)言它是面向?qū)ο蠖鳦語(yǔ)言是面向過(guò)程的,這是最大的不同,對(duì)于學(xué)過(guò)C語(yǔ)言的我們來(lái)說(shuō),Java可以說(shuō)是比較簡(jiǎn)單的編程語(yǔ)言
    2017-04-04
  • springboot集成Deepseek4j的項(xiàng)目實(shí)踐

    springboot集成Deepseek4j的項(xiàng)目實(shí)踐

    本文主要介紹了springboot集成Deepseek4j的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • 詳解如何在SpringBoot中使用WebMvc

    詳解如何在SpringBoot中使用WebMvc

    Spring?Boot?是一個(gè)快速、簡(jiǎn)單的開發(fā)框架,在?Spring?Boot?中,我們可以使用?WebMvc?來(lái)構(gòu)建?Web?應(yīng)用程序,所以本文就來(lái)講講如何在SpringBoot中使用WebMvc吧
    2023-06-06
  • SpringCloud基于RestTemplate微服務(wù)項(xiàng)目案例解析

    SpringCloud基于RestTemplate微服務(wù)項(xiàng)目案例解析

    這篇文章主要介紹了SpringCloud基于RestTemplate微服務(wù)項(xiàng)目案例,在寫SpringCloud搭建微服務(wù)之前,先搭建一個(gè)不通過(guò)springcloud只通過(guò)SpringBoot和Mybatis進(jìn)行模塊之間通訊,通過(guò)一個(gè)案例給大家詳細(xì)說(shuō)明,需要的朋友可以參考下
    2022-05-05
  • java servlet手機(jī)app訪問(wèn)接口(二)短信驗(yàn)證

    java servlet手機(jī)app訪問(wèn)接口(二)短信驗(yàn)證

    這篇文章主要介紹了java servlet手機(jī)app訪問(wèn)接口(二),短信驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • jenkins如何部署應(yīng)用到多個(gè)環(huán)境

    jenkins如何部署應(yīng)用到多個(gè)環(huán)境

    本文介紹了如何基于流水線的方式將應(yīng)用程序部署到多個(gè)環(huán)境,包括測(cè)試環(huán)境和生產(chǎn)環(huán)境,通過(guò)創(chuàng)建項(xiàng)目、設(shè)置參數(shù)、配置流水線、設(shè)置環(huán)境變量、配置Maven工具、構(gòu)建階段、部署測(cè)試環(huán)境和生產(chǎn)環(huán)境、以及清理階段,實(shí)現(xiàn)了自動(dòng)化部署流程
    2024-11-11

最新評(píng)論