optimistic-lock/src/test/java/top/lrshuai/optimisticlock/ThreadTest.java

package top.lrshuai.optimisticlock;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import top.lrshuai.optimisticlock.usr.dto.TransferDTO;
import top.lrshuai.optimisticlock.usr.service.IUserAccountService;

import java.math.BigDecimal;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ThreadTest {

    @Autowired
    private IUserAccountService userAccountService;

    /**
     * 并发测试重试机制
     *
     * @throws Exception
     */
    @Test
    public void test() throws Exception {

        TransferDTO dto = new TransferDTO();
        dto.setFromUserId(1001l);
        dto.setToUserId(1002l);
        dto.setAmount(BigDecimal.ONE);

        int clientTotal = 100;
        // 同时并发执行的线程数
        int threadTotal = 20;
        int count = 0;
        ExecutorService executorService = Executors.newCachedThreadPool();
        //信号量,此处用于控制并发的线程数
        final Semaphore semaphore = new Semaphore(threadTotal);
        //闭锁,可实现计数器递减
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    //执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,
                    //允许通行,否则线程阻塞等待,直到获取到许可。
                    semaphore.acquire();
                    userAccountService.transfter(dto);
                    //释放许可
                    semaphore.release();
                } catch (Exception e) {
                    //log.error("exception", e);
                    e.printStackTrace();

                }
                //闭锁减一
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行
        executorService.shutdown();
    }
}

optimistic-lock/src/main/java/top/lrshuai/optimisticlock/aspect/TryAgainAspect.java

package top.lrshuai.optimisticlock.aspect;

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.context.annotation.Configuration;
import org.springframework.transaction.annotation.Transactional;
import top.lrshuai.optimisticlock.common.ApiException;
import top.lrshuai.optimisticlock.common.ApiResultEnum;

/**
 * 更新失败,尝试重试切片
 * @author  rstyro
 */
@Aspect
@Configuration
public class TryAgainAspect {

	/**
	 * 默认重试几次
	 */
	private static final int    DEFAULT_MAX_RETRIES = 3;

	private int                 maxRetries          = DEFAULT_MAX_RETRIES;
	private int                 order               = 1;

	public void setMaxRetries(int maxRetries) {
		this.maxRetries = maxRetries;
	}

	public int getOrder() {
		return this.order;
	}

	@Pointcut("@annotation(IsTryAgain)")
	public void retryOnOptFailure() {
		// pointcut mark
	}

	@Around("retryOnOptFailure()")
	@Transactional(rollbackFor = Exception.class)
	public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
		int numAttempts = 0;
		do {
			numAttempts++;
			try {
				//再次执行业务代码
				return pjp.proceed();
			} catch (TryAgainException ex) {
				if (numAttempts > maxRetries) {
					//log failure information, and throw exception
//					如果大于 默认的重试机制 次数,我们这回就真正的抛出去了
					throw new ApiException(ApiResultEnum.ERROR_TRY_AGAIN_FAILED);
				}else{
					//如果 没达到最大的重试次数,将再次执行
					System.out.println("=====正在重试====="+numAttempts+"次");
				}
			}
		} while (numAttempts <= this.maxRetries);

		return null;
	}
}

optimistic-lock/src/main/java/top/lrshuai/optimisticlock/aspect/TryAgainException.java

package top.lrshuai.optimisticlock.aspect;

import top.lrshuai.optimisticlock.common.ApiException;
import top.lrshuai.optimisticlock.common.ApiResultEnum;

/**
 * 更新重试异常
 */
public class TryAgainException extends ApiException {

    public TryAgainException(ApiResultEnum apiResultEnum) {
        super(apiResultEnum);
    }

}

optimistic-lock/src/main/java/top/lrshuai/optimisticlock/common/ApiException.java

package top.lrshuai.optimisticlock.common;


import lombok.Data;

/**
 * 自定义的api异常
 * @author rstyro
 *
 */
@Data
public class ApiException extends RuntimeException{
	private static final long serialVersionUID = 1L;
	private int status;
	private String message;
	private Object data;
	private Exception exception;
	public ApiException() {
		super();
	}

	public ApiException(String message) {
		super(message);
		this.message=message;
	}

	public ApiException(int status, String message, Object data, Exception exception) {
		super(message,exception);
		this.status = status;
		this.message = message;
		this.data = data;
		this.exception = exception;
	}
	public ApiException(ApiResultEnum apiResultEnum) {
		this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),null,null);
	}
	public ApiException(ApiResultEnum apiResultEnum, Object data) {
		this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),data,null);
	}
	public ApiException(ApiResultEnum apiResultEnum, Object data, Exception exception) {
		this(apiResultEnum.getStatus(),apiResultEnum.getMessage(),data,exception);
	}


}

optimistic-lock/src/main/java/top/lrshuai/optimisticlock/common/ApiResultEnum.java

package top.lrshuai.optimisticlock.common;

/**
 * 异常 返回码
 */
public enum ApiResultEnum {
	SUCCESS(200,"ok"),
	FAILED(400,"请求失败"),
	ERROR(500,"服务器错误"),
	ERROR_NULL(501,"空指针异常"),
	ERROR_CLASS_CAST(502,"类型转换异常"),
	ERROR_RUNTION(503,"运行时异常"),
	ERROR_IO(504,"上传文件异常"),
	ERROR_MOTHODNOTSUPPORT(505,"请求方法错误"),
	ERROR_TRY_AGAIN(506,"正在重试"),
	ERROR_TRY_AGAIN_FAILED(507,"重试失败"),


	//参数
	PARAMETER_NULL(10001,"缺少参数或值为空"),
	TRIGGER_GROUP_AND_NAME_SAME(10002,"组名和名称已存在"),



	//账户
	ACCOUNT_LOCK(20001,"账号已锁定"),
	ACCOUNT_NOT_FOUND(20002,"找不到账户信息"),
	ACCOUNT_PASSWARD_ERROR(20003,"用户名密码错误"),
	ACCOUNT_EXIST(20004,"账号已存在"),
	ACCOUNT_NOT_SUFFICIENT(20005,"账号余额不足"),

	//权限
	AUTH_NOT_HAVE(30001,"没有权限"),
	AUTH_SIGN_NOT_MATCH(30002,"签名不匹配"),



	FILE_IS_NULL(40001,"文件为空"),
	FILE_NOT_PIC(40002,"不是图片类型文件"),

	TASK_IS_RUNING(50001,"任务已启动,无法再起启动"),
	TASK_IS_PAUSE(50002,"任务暂停,只可继续执行"),
	TASK_NOT_RUNING(50003,"任务未执行,无法暂停"),


	EMS_CODE_NOT_FOUND(60000,"物流编号找不到,请填写物流编号"),
	;

	private int status;
	private String message;
	
	public String getMessage() {
		return message;
	}

	public int getStatus() {
		return status;
	}
	private ApiResultEnum(int status,String message) {
		this.status = status;
		this.message = message;
	}

	
}

optimistic-lock/src/main/java/top/lrshuai/optimisticlock/config/

package top.lrshuai.optimisticlock.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import top.lrshuai.optimisticlock.aspect.TryAgainException;
import top.lrshuai.optimisticlock.common.ApiException;
import top.lrshuai.optimisticlock.common.ApiResultEnum;
import top.lrshuai.optimisticlock.common.Result;

import java.io.IOException;

/**
 * 全局异常捕获
 * @author rstyro
 * @since 2019-03-12
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

	private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

	@ExceptionHandler(NullPointerException.class)
	public Result NullPointer(NullPointerException ex){
	   logger.error(ex.getMessage(),ex);
	   return Result.error(ApiResultEnum.ERROR_NULL);
    }

    @ExceptionHandler(ClassCastException.class)
    public Result ClassCastException(ClassCastException ex){
        logger.error(ex.getMessage(),ex);
        return Result.error(ApiResultEnum.ERROR_CLASS_CAST);
    }

    @ExceptionHandler(IOException.class)
    public Result IOException(IOException ex){
        logger.error(ex.getMessage(),ex);
        return Result.error(ApiResultEnum.ERROR_IO);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex){
        logger.error(ex.getMessage(),ex);
        return Result.error(ApiResultEnum.ERROR_MOTHODNOTSUPPORT);
    }

    @ExceptionHandler(TryAgainException.class)
    public Result TryAgainException(TryAgainException ex) {
	    logger.error("全局异常========正在重试");
        return Result.error(ex.getStatus(),ex.getMessage());
    }


    @ExceptionHandler(ApiException.class)
    public Result ApiException(ApiException ex) {
        logger.error(ex.getMessage(),ex);
        return Result.error(ex.getStatus(),ex.getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    public Result RuntimeException(RuntimeException ex){
        logger.error(ex.getMessage(),ex);
        return Result.error(ApiResultEnum.ERROR_RUNTION);
    }

    @ExceptionHandler(Exception.class)
    public Result exception(Exception ex){
        logger.error(ex.getMessage(),ex);
        return Result.error(ApiResultEnum.ERROR);
    }

}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐