代码如下,add 方法中有一个 synchronized 代码块,锁定的是 uid 字符串,代码中的逻辑也很简单,先查询没有则新增,有则不做操作。但是这个方法会出现重复新增的情况,也就是没锁住,多个线程进来了。发生这种情况的数据量也很少,大概几千条数据就一两条出现这种情况.大家有什么看法吗?
补充一下:出现上述并发情况下打印过锁定对象的内存地址值,结果发现地址值也是一样的。
public class StringPoolUtil {
private static final Interner<String> POOL = Interners.newWeakInterner();
public static Interner<String> getPool(){
return POOL;
}
}
public void add(String uid){
synchronized (StringPoolUtil.getPool().intern(uid)) {
// 查询数据
// 没有数据则新增
}
}
1
GThui 2022-11-22 11:30:34 +08:00 via Android
肯定锁对象不安全了。可能 uid 或者 StringPoolUtil 方法可能和 gc 也有关系
|
3
dqzcwxb 2022-11-22 11:45:05 +08:00
项目部署了几个
|
5
lovedoing 2022-11-22 11:58:27 +08:00
Interners 里面的 String 也是会被 gc 的
|
6
Hurriance 2022-11-22 12:18:15 +08:00
「没有数据则新增」这里可以打个日志看看吗?可能是其他线程真的也没查到数据?
|
7
codehz 2022-11-22 13:16:11 +08:00
虽然不是很懂 interner ,但是稍微查看了下文档,这应该要用 newStrongInterner 才对吧。。。
|
10
zilongzixue 2022-11-22 16:08:33 +08:00
直接上 redisson 分布式锁好了,一步到位
|
11
sprit 2022-11-22 16:13:22 +08:00
有无可能不是 SYNC 的问题,是 MySQL 的问题[dog]
|
12
liudaolunhuibl 2022-11-22 16:24:35 +08:00
看一下“StringPoolUtil.getPool().intern(uid)”这个呢?
为什么不直接 uid.intern()呢? |
13
liudaolunhuibl 2022-11-22 17:01:02 +08:00
jdk7 之后可以通过-XX:StringTableSize 参数来修改 StringTable 的大小,先试试改为用 String 的 intern 吧,你这个本来也是 String 对象
|
14
liudaolunhuibl 2022-11-22 17:09:10 +08:00
也可以换成 Interners.newStrongInterner()
|
15
exqibao 2022-11-22 17:10:45 +08:00
会不会是 synchronized 块已经结束了,但事务还没有提交。
|
16
corningsun 2022-11-22 17:21:51 +08:00
数据库加唯一索引吧?
|
17
suStudent 2022-11-22 17:37:23 +08:00
弱引用:弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只 被弱引用关联的对象。
怀疑多次加锁时,中间经历过一次垃圾回收。 |
19
night98 2022-11-23 03:43:08 +08:00
楼上说的都挺有道理,我补充一条吧,sync 应该包裹 add 方法而不是在 add 方法体内,否则会存在极小的时间差即 return 后事务注解执行 commit 方法前进入方法
|
20
suStudent 2022-11-23 09:18:25 +08:00
@fronted 对应地址值? hashCode()? hashCode 可能相等。网上的例子"重地"和"通话" hashCode 值是一样的
|
24
fronted OP @liudaolunhuibl 我先试试你的做法
|
25
312ybj22 2022-11-23 11:19:49 +08:00
在并发情况也要考虑下数据库的问题, 先看下数据库的隔离级别,在并发情况下事务还没提交,redo log 还没写到内存里,下个请求从数据库是读不到的,这样子就有并发问题了, 你可以改成编程式事务,手动提交后,再结束这把锁
|
26
buster 2022-11-23 11:45:23 +08:00
看了你的思路,我觉得你可以考虑一下,构建一个 Object[] 长度=16 ,然后你的 hashcode 对 16 取模,决定获取第几个 object 来加 sync 锁,觉得能满足你的需求
|
28
exqibao 2022-11-23 12:19:09 +08:00
大概就是假如 save 方法开启事务,add 方法就是带有 sync 的方法,save 方法里面调用 add 方法。
这时 add 结束了,sync 锁已经释放了,但 save 方法还没结束,还没提交事物。 这样 sync 释放到事务 commit 之间,假如来一样的数据,sync 锁过了,查数据库也没数据,数据就重复提交了。 |
29
312ybj22 2022-11-23 17:02:33 +08:00
@fronted 这问题就很有意思了, 用我组长的话来讲“这个一道很好的面试题”, 这样吧,你先试试用低级别的隔离级别,比如 RC ,这样并发度高些,看看会不会有重复的问题。 另外你查询的时候最好走索引,能用行锁用行锁。 先把问题解决,我后面也研究下这个问题,有结果我扣你
|
30
Red998 2022-11-23 18:14:06 +08:00
加个唯一索引 重复问题。按道理 一般的锁都是 object 你这个写法少见。锁也就是对象头的一个标识。建议不用 String
|