并发情况下为插入记录操作添加同步锁

时间:2022-10-06 19:35:30

最近在为一个客户进行系统的二开,系统是一个在线教育平台,主要功能为在线视频观看,视频托管在第三方视频播放平台,系统负责记录学员的视频观看记录。


 在系统运行中突然发现无法观看视频了,经过debug发现,原来是因为出现重复的视频记录,如下图

并发情况下为插入记录操作添加同步锁

这个表本应该由watch_user_id,lesson_id,video_id三列设置联合唯一主键,但是偏偏就这么操蛋没有设置,然后这个出现重复记录还不是个例,仔细分析一下,因为记录id都是连续的,考虑是因为并发造成记录的重复插入,接着就去看代码,发现果然是没有加同步锁

这样子就这然而然的给这个插入操作(watchVideo方法)添加一个同步锁:

synchronized(this){
  ProjectUserWatchMapper.insert(projectUserWatch);
}

以为万事大吉,发布测试;然而。。。。结果是残酷的,后面请教dalao,dalao看了我写的同步锁,默默看了我一眼,说这个同步锁写的不对,因为项目本身在service层已经添加了事务,我写的那个同步锁不起作用,所以应该要自己用一个map,以用户userId做key,对用户的观看记录进行加锁,防止观看记录的重复插入操作

String userStrId = IdMapUtils.getIdForMap(String.valueOf(userId));
synchronized (userStrId) {
  projectUserWatchService.watchVideo(userId, id, currVideo);
}

public class IdMapUtils {

  /**
  * 用户id map
  */
  private static ConcurrentMap<String, String> idMap = new ConcurrentHashMap<>();

  public static synchronized String getIdForMap(String id) {
    if(idMap.containsKey(id)) {
      return idMap.get(id);
    }else {
      idMap.put("id", id);
      return id;
    }
  }
}

用ConcurrentHashMap是因为它是线程安全的,HashMap是线程不安全的,需要自己加锁,还有一个需要注意的地方,map中get(key)==null不代表key不在这个map中,有可能是因为这个key的value为null,所以判断key在不在map应该使用containsKey方法