同事突然问了一个这样的问题,自己写在代码里的更新语句
update virtual_assets set amount = amount +1 where user_id='test';
先说结论:没有线程安全问题。
脑子突然想不明白,猜测可能不存在线程安全问题.还是自己实践一下最保险。(个人猜测和mysql的MVCC机制有关,网上也有说update会加排它锁,阻塞下一条update语句的执行)
@Update({"update virtual_assets",
"set amount = amount + 1",
"where user_id=#{userId}"
})
void addTest(@Param("userId") String userId);
@Select({"select amount",
"from virtual_assets",
"where user_id = #{userId}"
})
Integer find(@Param("userId")String userId);
@Update({"update virtual_assets",
"set amount = #{amount}",
"where user_id=#{userId}"
})
void add(@Param("userId") String userId,@Param("amount") Integer amount);
一种update的时候就加,一种先查再加。
@Test
public void threadTest(){
String userId= "test";
String userId2= "test2";
for(int i=0;i<100;i++){
Thread thread = new Thread(() -> {
virtualAssetsDao.addTest(userId);
virtualAssetsDao.addTest2(userId2);
});
thread.start();
}
try {
Thread.sleep(100000);
} catch (Exception e){
System.out.println("sssss");
}
}
注意test里多线程必须让主线程先等一下,不然主线程结束,子线程还没执行完也不会继续执行。
执行结果如下,初始值都是0,
一个语句的话没有线程安全问题,两个语句分开执行就有问题,需要开启事务才行。
然后又试了一下in语句,也同样没有问题。注意mybatis动态注解写in语句比较麻烦,可以参考in语句写法
@Update({
"<script>",
"update virtual_assets set amount = amount + 1 where user_id in ",
"<foreach collection=\"userIds\" item=\"userId\" index=\"index\" open=\"(\" separator=\",\" close=\")\">",
"#{userId}",
"</foreach>",
"</script>"
})
void addTest(@Param("userIds") List<String> userId);
@Test
public void threadTest(){
List<String> ids = new ArrayList<>();
String userId= "test";
String userId2= "test2";
ids.add(userId);
ids.add(userId2);
//virtualAssetsDao.addTest(ids);
for(int i=0;i<100;i++){
int finalI = i;
Thread thread = new Thread(() -> {
virtualAssetsDao.addTest(ids);
//virtualAssetsDao.addTest2(userId2);
System.out.println(finalI);
});
thread.start();
}
try {
Thread.sleep(100000);
} catch (Exception e){
System.out.println("sssss");
}
}
最后执行两次后数据库数据为
同时控制台输出
看到线程确实是乱序执行,但是最后的结果没问题。