io.lettuce.core.RedisCommandInterruptedException: Command interrupted java指令重排序引发的bug

报错log:

2020/11/26 17:55:08 ERROR [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler] - Unexpected error occurred in scheduled task
org.springframework.data.redis.RedisSystemException: Redis command interrupted; nested exception is io.lettuce.core.RedisCommandInterruptedException: Command interrupted
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:62)
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
	at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
	at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
	at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:270)
	at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.convertLettuceAccessException(LettuceStreamCommands.java:472)
	at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.xAdd(LettuceStreamCommands.java:109)
	at org.springframework.data.redis.connection.DefaultedRedisConnection.xAdd(DefaultedRedisConnection.java:451)
	at org.springframework.data.redis.connection.DefaultStringRedisConnection.xAdd(DefaultStringRedisConnection.java:3763)
	at org.springframework.data.redis.core.DefaultStreamOperations.lambda$add$1(DefaultStreamOperations.java:135)
	at org.springframework.data.redis.core.DefaultStreamOperations$$Lambda$737/411704343.doInRedis(Unknown Source)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
	at org.springframework.data.redis.core.DefaultStreamOperations.add(DefaultStreamOperations.java:135)
	at org.springframework.data.redis.core.StreamOperations.add(StreamOperations.java:110)
	at com.SpringLearn.common.redis.JedisUtils.xadd(JedisUtils.java:740)
	at com.SpringLearn.common.messageQueue.BaseMessageTrigger.send(BaseMessageTrigger.java:33)
	at com.SpringLearn.service.xxxx.xxxxx.insert(PlayerActionService.java:197)
	at com.SpringLearn.service.xxxxx.xxxxx$$FastClassBySpringCGLIB$$567a93ae.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$707/1475341560.proceedWithInvocation(Unknown Source)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.SpringLearn.service.xxxx.xxxxx$$EnhancerBySpringCGLIB$$faf84b2b.insert(<generated>)
	at com.SpringLearn.service.xxxxx.xxxxxx.fold(PlayerActionService.java:178)
	at com.SpringLearn.service.xxxxx.xxxxxx.fold(PlayerActionService.java:154)
	at com.SpringLearn.service.xxxxx.xxxxxxxxx.sitOutGame(PlayerActionService.java:217)
	at com.SpringLearn.service.xxxxxx.xxxxxxxxxx$$FastClassBySpringCGLIB$$567a93ae.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$707/1475341560.proceedWithInvocation(Unknown Source)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.SpringLearn.service.player.PlayerActionService$$EnhancerBySpringCGLIB$$faf84b2b.sitOutGame(<generated>)
	at com.SpringLearn.service.stopWatch.excuter.PlayerActStopWatch.callBack(PlayerActStopWatch.java:43)
	at com.SpringLearn.service.stopWatch.StopWatchService$StopWatchRunnable.run(StopWatchService.java:79)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: io.lettuce.core.RedisCommandInterruptedException: Command interrupted
	at io.lettuce.core.protocol.AsyncCommand.await(AsyncCommand.java:87)
	at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:112)
	at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69)
	at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
	at com.sun.proxy.$Proxy136.xadd(Unknown Source)
	at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.xAdd(LettuceStreamCommands.java:106)
	... 58 common frames omitted
Caused by: java.lang.InterruptedException: null
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2258)
	at io.lettuce.core.protocol.AsyncCommand.await(AsyncCommand.java:83)
	... 63 common frames omitted

lettuce作为redis的驱动在进行xadd命令时报错:Command interrupted(指令被中断),有时候是druid报同样的错误,两个错误相同点在下面这个图中,都是这里报了InterruptedException
在这里插入图片描述
CompletableFuture.get方法详情:
在这里插入图片描述
报错是因为Thread.interrupted()的结果是true,Thread.interrupted()方法的作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
当前线程为什么会被中断呢?苦苦思索后找到了原因:

@Slf4j
@Service
public class StopWatchService {

	@Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
 
    /**
     * 存放动态定时任务结果,用于关闭定时任务
     */
    private ConcurrentHashMap<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<String, ScheduledFuture<?>>();
    
    /**
     * 
     * @Description:添加定时任务 
     * @param stopWatch      
     */
    public void addStopWatch(BaseStopWatchExcuter stopWatch) {
    	ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(new StopWatchRunnable(stopWatch), stopWatch.getStartTime());
    	futureMap.put(stopWatch.getKey(), schedule);
    }
    
    /**
     * 
     * @Description:取消 
     * @param stopWatch
     * @param key    
     */
    public void cancel(String key) {
    	try {			
    		ScheduledFuture<?> scheduledFuture = futureMap.get(key);
    		if(null != scheduledFuture) {    			
    			scheduledFuture.cancel(true);
    			futureMap.remove(key);
    		}
		} catch (Exception e) {
			log.error(e.getMessage());
		}
    }
    

    /**
     * 内部类--定时任务线程
     */
    private class StopWatchRunnable implements Runnable {
    	
    	private BaseStopWatchExcuter stopWatch;
    	
    	public StopWatchRunnable(BaseStopWatchExcuter stopWatch) {
    		this.stopWatch = stopWatch;
    	}
    	
        @Override
        public void run() {
            // 执行完后要取消该定时任务
        	this.stopWatch.callBack();
            cancel(this.stopWatch.getKey());
        }
    }
}

这个服务是一个基于org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler,用于设定一个定时器,在指定时间点运行StopWatchRunnable线程,线程内的run方法会调用具体的StopWatchExcuter 回调方法,然后调用cancel方法。
cancel方法是为了外部逻辑能手动取消定时任务,方法中根据key从futureMap中找到ScheduledFuture,调用ScheduledFuture.cancel(boolean mayInterruptIfRunning)方法,删除map中的记录。
mayInterruptIfRunning参数为true表示不论StopWatchRunnable 线程是否运行结束都会被终止,导致Thread.interrupted()返回true的原因就在这里。
并且通过在cancel方法和callBack方法中输出日志的顺序看出,这里进行了指令重排序,将mayInterruptIfRunning参数设置为false解决了报错

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值