关于 synchronized 锁定对象出现的问题

2022-11-22 11:24:46 +08:00
 fronted

代码如下,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)) {
    // 查询数据
    // 没有数据则新增
    }
}
2685 次点击
所在节点    程序员
30 条回复
GThui
2022-11-22 11:30:34 +08:00
肯定锁对象不安全了。可能 uid 或者 StringPoolUtil 方法可能和 gc 也有关系
fronted
2022-11-22 11:34:44 +08:00
@GThui 打印过锁定对象的地址值,都是一样的
dqzcwxb
2022-11-22 11:45:05 +08:00
项目部署了几个
fronted
2022-11-22 11:45:41 +08:00
@dqzcwxb 单项目哈
lovedoing
2022-11-22 11:58:27 +08:00
Interners 里面的 String 也是会被 gc 的
Hurriance
2022-11-22 12:18:15 +08:00
「没有数据则新增」这里可以打个日志看看吗?可能是其他线程真的也没查到数据?
codehz
2022-11-22 13:16:11 +08:00
虽然不是很懂 interner ,但是稍微查看了下文档,这应该要用 newStrongInterner 才对吧。。。
fronted
2022-11-22 14:38:56 +08:00
@Hurriance 是其他线程没有查到数据,但是锁定的同一个对象,没释放锁其他线程不知道怎么的也进来了
fronted
2022-11-22 16:04:41 +08:00
@codehz 有道理,但是我这里出现的这个问题没有发生 gc 这种情况
zilongzixue
2022-11-22 16:08:33 +08:00
直接上 redisson 分布式锁好了,一步到位
sprit
2022-11-22 16:13:22 +08:00
有无可能不是 SYNC 的问题,是 MySQL 的问题[dog]
liudaolunhuibl
2022-11-22 16:24:35 +08:00
看一下“StringPoolUtil.getPool().intern(uid)”这个呢?

为什么不直接 uid.intern()呢?
liudaolunhuibl
2022-11-22 17:01:02 +08:00
jdk7 之后可以通过-XX:StringTableSize 参数来修改 StringTable 的大小,先试试改为用 String 的 intern 吧,你这个本来也是 String 对象
liudaolunhuibl
2022-11-22 17:09:10 +08:00
也可以换成 Interners.newStrongInterner()
exqibao
2022-11-22 17:10:45 +08:00
会不会是 synchronized 块已经结束了,但事务还没有提交。
corningsun
2022-11-22 17:21:51 +08:00
数据库加唯一索引吧?
suStudent
2022-11-22 17:37:23 +08:00
弱引用:弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只 被弱引用关联的对象。

怀疑多次加锁时,中间经历过一次垃圾回收。
fronted
2022-11-22 17:42:21 +08:00
@suStudent 应该不至于 我在进入 synchronized 打印过锁定的对象地址,值都是一样的
night98
2022-11-23 03:43:08 +08:00
楼上说的都挺有道理,我补充一条吧,sync 应该包裹 add 方法而不是在 add 方法体内,否则会存在极小的时间差即 return 后事务注解执行 commit 方法前进入方法
suStudent
2022-11-23 09:18:25 +08:00
@fronted 对应地址值? hashCode()? hashCode 可能相等。网上的例子"重地"和"通话" hashCode 值是一样的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/897027

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX