记录一次生产环境的多线程并发问题

1.当时的场景

开一个线程,定时check hbase,避免hbase异常时,阻塞大量请求。

2.程序伪代码

伪代码如下:
定时用的是jdk自带的工具类 Timer

具体逻辑:

private ExecutorService executorService = Executors.newSingleThreadExecutor();
public void run(){
	Future<Boolean> future = null;
	List<CheckResult> checkList = new Arraylist();
	while(){//避免由于网络问题导致check不准确,所以失败时会重试三次
		try{
			// CheckThread里面是具体的check逻辑,会将check的结果放到checkList中
			// 为了防止check超时,所以采用线程池的submit方法,返回一个future
			future = executorService.submit(new CheckThread(checkList));
			Boolean result = future.get(30,TimeUnit.SECONDS);
		}catch (Exception e){
			if(future != null){
				// 有异常时 中断CheckThread执行
				future.cancel(true);				
			}
		}
	}
	// 增强for循环 遍历 CheckList,得到最终的check结果缓存
	setCheckResult(checkList);	
}

到这,不知道你们能不能看出来并发问题?

3.导致的问题

将程序上线到prd的问题:Timer停止运行,hbase某个RegionServer恢复正常后,由于check停止了,没有恢复RegionServer的状态,导致了部分请求到该RegionServer的请求异常。查日志,并发问题出在checkList上

4.并发问题

CheckThread是由线程池中的线程执行,在执行异常时虽然调用了future.cancel(true);但是这个方法只是给打上了中断标记,并不能保证一定能中断线程执行。也就是说当此线程在往checkList增加内容的同时;
setCheckResult(checkList)是由主线程执行,这个方法正在遍历checkList:

for(CheckResult result : checkList){
}

增强for循环是采用的迭代器遍历,也就是说在遍历过程中如果list中的元素个数有变化就会报错(fast-fail)。

一个线程在添加内容,一个线程在用增强for循环遍历,最终也就导致了并发问题。

5.解决方案

知道原因,也就很好找解决方案了。
以上主要有两个问题,一个是Timer停止问题,一个是并发问题,当然是因为并发问题导致的Timer停止
其实在我们这个场景下,只要Timer不停止,就算出现并发问题,影响也不大,因为check定时10秒就会执行一次,后面check结果就正常了,毕竟并发问题出现的概率不高。

解决办法:
1.将run()方法的所有方法体给try catch,不要往外抛异常(Timer执行过程中有异常就会停止执行)
2.最简单的方式,也是我采用的方式:重新new一个List对象:

//这个方法最终采用的是native方法:System.arraycopy();
List<CheckResult> list = new ArrayList(checkList);

到这就完了,这也更加警示了自己,只要涉及到多线程,就一定要小心谨慎,多多思考。因为这种问题一般测试时测不出来的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值