最近我们发现在多并发情况下,发现Jfinal会有线程不安全的情况。我们先按照之前的文档做如下处理
public class SafeTestCase {
public static SafeTestCase me = new SafeTestCase();
public void signInTask(final int userId) {
try {
boolean isSigned = checkSignIn(userId, RuleID);
Db.tx(() -> {
if (!isSigned) {
System.out.println(userId + "加积分");
addScore(userId,RuleID, RuleScore);
}
return true;
});
} catch (Exception e) {
}
}
private static final String lock = "lock";
public void signInTaskLock(final int userId) {
try {
synchronized (lock) {
boolean isSigned = checkSignIn(userId, RuleID);
Db.tx(() -> {
if (!isSigned) {
System.out.println(userId + "加积分");
addScore(userId,RuleID, RuleScore);
}
return true;
});
}
} catch (Exception e) {
}
}
}业务逻辑,一个用户一天只能签到一次并增加积分,isSigned为true时说明用户已经签到。
第一个方法是按照官方文档方法进行数据存储处理的,
第二个方法是我们按照加锁处理线程安全。
然后在controller层对以上两个方法进行调用看结果。
@ActionKey("safeTest")
public void safeTest() {
int userId = getParaToInt();
SafeTestCase.me.signInTask(userId);
renderText("ok");
}
@ActionKey("safeTestLock")
public void safeTestLock() {
int userId = getParaToInt();
SafeTestCase.me.signInTaskLock(userId);
renderText("ok");
}以下是测试类:
@Testpublic void safeCaseTest() {
int userId = 30000;
String url="http://localhost:8080/safeTest/"+userId;
String urlLock="http://localhost:8080/safeTestLock/"+(userId+1);
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i = 0; i<=10;i++){
executorService.execute(new Runnable() {
@Override
public void run() {
HttpKit.get(url);
HttpKit.get(urlLock);
}
});
}
try {
Thread.sleep(200000);
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}结果发现第一种方法会出现多个重复记录,第二种方法由于加了锁没有出现重复记录。
麻烦波神看下,如果要做线程安全的话,如何运用框架处理,而不是加锁。