今天写了一行代码,觉得完事大吉,结果在真正执行的时候发现无法实现对qId的串行操作,当时就懵逼了。
原始代码:
public JsonResponse synVerifying(Integer qId, BigDecimal score, FanyaUserDetail user, Map<String, String> map) { //相同的qId会排序 synchronized (qId) { try { //判断是否因网络延迟而造成的单题重复点击,针对同一个用户 boolean verified = questionVerifyService.isVerified(qId, user.getUid()); if (verified) { Integer counts = questionVerifyService.getCountByQid(qId); //不同用户同时审核同一道题时,审核次数大于3,则不用审核了 if (counts >= 3) { return JsonResponse.failure().put("failMsg", "该题已审核完毕"); } else { verifyService.verifying(user.getUid(), qId, score, map.get("realName"), map.get("orgName"), counts); return new JsonResponse(JsonResponse.CODE_SUCCESS, "ok"); } } else { return JsonResponse.failure().put("failMsg", "请勿重复点击"); } } catch (Exception e) { e.printStackTrace(); return JsonResponse.failure().put("failMsg", e.getMessage()); } } }
修改后代码:
public JsonResponse synVerifying(Integer qId, BigDecimal score, FanyaUserDetail user, Map<String, String> map) { //相同的qId会排序 synchronized (qId.toString().intern()) { try { //判断是否因网络延迟而造成的单题重复点击,针对同一个用户 boolean verified = questionVerifyService.isVerified(qId, user.getUid()); Thread.sleep(5000); System.out.println("----------------------"); System.out.println(verified); System.out.println("======================="); if (verified) { Integer counts = questionVerifyService.getCountByQid(qId); //不同用户同时审核同一道题时,审核次数大于3,则不用审核了 if (counts >= 3) { return JsonResponse.failure().put("failMsg", "该题已审核完毕"); } else { verifyService.verifying(user.getUid(), qId, score, map.get("realName"), map.get("orgName"), counts); return new JsonResponse(JsonResponse.CODE_SUCCESS, "ok"); } } else { return JsonResponse.failure().put("failMsg", "请勿重复点击"); } } catch (Exception e) { e.printStackTrace(); return JsonResponse.failure().put("failMsg", e.getMessage()); } } }
查了很多资料,发现原来由于Integer的自动装箱和拆箱在作怪。当数据>=127时,就不会放到常量池,而是从新new一个对象。
public static Integer valueOf(int i) { assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
而使用String.intern()方法就是判断常量池中是否有该数据,有就返回该字符串。
intern
public String intern()
-
返回字符串对象的规范化表示形式。
一个初始为空的字符串池,它由类
String
私有地维护。当调用 intern 方法时,如果池已经包含一个等于此
String
对象的字符串(用equals(Object)
方法确定),则返回池中的字符串。否则,将此String
对象添加到池中,并返回此String
对象的引用。它遵循以下规则:对于任意两个字符串
s
和t
,当且仅当s.equals(t)
为true
时,s.intern() == t.intern()
才为true
。所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。字符串字面值在 Java Language Specification 的 §3.10.5 定义。
-
-
返回:
- 一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。
注意:这里锁住的qId其实是一个对象,所以synchronized锁住的不是字段,而是对象。而要保证相同的值是同一个对象,因此要放入常量池中。