最近我们发现在多并发情况下,发现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(); } }
结果发现第一种方法会出现多个重复记录,第二种方法由于加了锁没有出现重复记录。
麻烦波神看下,如果要做线程安全的话,如何运用框架处理,而不是加锁。