在需求设计中需要防止并发出现重复处理,导致数据不一致的情况。可以使用锁+等待重试的机制来实现。下面是模拟这种需求的一个例子,我是使用的基于AQS实现的轻量级锁ReentrantLock 来实现,但是如果是分布式系统,是不适合的,在真实场景中,我是用redis来实现的分布式锁,主要是用到了jedisCluster的原子方法set(NX+PX),这里就不做这部分介绍了。
废话不多说,直接看代码:
public static void main(String[] args) {
CorpServicePayRecordEntity servicePayRecord = new CorpServicePayRecordEntity();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(pout(5, servicePayRecord));
}
});
t1.start();
t1.setName("thread1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(pout(5, servicePayRecord));
}
});
t2.setName("thread2");
t2.start();
}
private static final ReentrantLock LOCK = new ReentrantLock();
private static String pout(int retry, CorpServicePayRecordEntity servicePayRecord){
System.out.println("线程"+ Thread.currentThread().getName() + "尝试");
if(LOCK.tryLock()){
System.out.println("线程"+ Thread.currentThread().getName() + "获取成功");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
LOCK.unlock();
}
return Thread.currentThread().getName();
}else {
try {
Thread.sleep(2000L);
retry--;
if(retry > 0){
return pout(retry, servicePayRecord);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "nothing";
}
意思是如果线程获取到了锁,那么就执行输出,其他线程则等待锁,并且重试(可以设置次数),这里使用了sleep来模拟执行的时间,当线程执行完会释放锁,其他线程有机会获取到对象锁,则执行,否则继续等待重试。
这里有个地方注意下,就是返回,刚刚开始测试的时候发现第二个线程获取到锁后怎么也打印不出自己的线程名字(return Thread.currentThread().getName();)这步是要打印出来线程名称才对,但是就是打印不了,总是打印最后的nothing。后来发现原来return pout(retry, servicePayRecord);刚刚开始没有加return,也是无语,以为上面有return了就不需要了,其实不然,了解jvm的内存结构就清楚了, 这个可能会被忽略。