关于 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)) {
    // 查询数据
    // 没有数据则新增
    }
}
2686 次点击
所在节点    程序员
30 条回复
fronted
2022-11-23 09:45:45 +08:00
@night98 主要是我想针对每个 uid 加锁,而不是调用该方法的用户都进行加锁
fronted
2022-11-23 09:48:55 +08:00
@suStudent 出现这种情况的时候锁对象的 hashcode 与值都是一样的
fronted
2022-11-23 09:49:45 +08:00
@exqibao 这种一般是怎么引起的呢,我这边暂时无法重现这种情况
fronted
2022-11-23 09:50:46 +08:00
@liudaolunhuibl 我先试试你的做法
312ybj22
2022-11-23 11:19:49 +08:00
在并发情况也要考虑下数据库的问题, 先看下数据库的隔离级别,在并发情况下事务还没提交,redo log 还没写到内存里,下个请求从数据库是读不到的,这样子就有并发问题了, 你可以改成编程式事务,手动提交后,再结束这把锁
buster
2022-11-23 11:45:23 +08:00
看了你的思路,我觉得你可以考虑一下,构建一个 Object[] 长度=16 ,然后你的 hashcode 对 16 取模,决定获取第几个 object 来加 sync 锁,觉得能满足你的需求
fronted
2022-11-23 11:51:20 +08:00
@312ybj22 你这种确实是有道理的,不过我好奇的是,那不是会出现有并发的情况要进行编程式事务处理
exqibao
2022-11-23 12:19:09 +08:00
大概就是假如 save 方法开启事务,add 方法就是带有 sync 的方法,save 方法里面调用 add 方法。
这时 add 结束了,sync 锁已经释放了,但 save 方法还没结束,还没提交事物。
这样 sync 释放到事务 commit 之间,假如来一样的数据,sync 锁过了,查数据库也没数据,数据就重复提交了。
312ybj22
2022-11-23 17:02:33 +08:00
@fronted 这问题就很有意思了, 用我组长的话来讲“这个一道很好的面试题”, 这样吧,你先试试用低级别的隔离级别,比如 RC ,这样并发度高些,看看会不会有重复的问题。 另外你查询的时候最好走索引,能用行锁用行锁。 先把问题解决,我后面也研究下这个问题,有结果我扣你
Red998
2022-11-23 18:14:06 +08:00
加个唯一索引 重复问题。按道理 一般的锁都是 object 你这个写法少见。锁也就是对象头的一个标识。建议不用 String

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

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

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

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

© 2021 V2EX