并发投票为什么没有出错

我弄了个投票功能,想测试一下并发下会不会出错,我不懂为什么没出现少投票的情况,一个人修改数据库的过程中,时候另一个人刚好修改了,
数据不应该小于总的累加数吗??
是不是我测试的有问题??
希望大佬解答一下!!
语句是

UPDATE candidates set ticket_num = REPLACE(ticket_num,ticket_num,ticket_num+1)
          WHERE u_id=#{u_id} and  p_id=#{p_id}

写了个测试方法

 @Test
    void vote1000() throws InterruptedException {
        Candidates candidates=new Candidates(2017010303,1);
        ArrayList<Thread> arrayList=new ArrayList();
        for (int i = 0; i < 1000; i++) {
            Thread thread=new Thread(()->{
                candidatesMapper.SiUpdateTicket(candidates);
            });
            arrayList.add(thread);
        }
       new Thread(()->{
           for (int i = 0; i < 500; i++) {
               arrayList.get(i).start();
           }
       }).start();
        new Thread(()->{for (int i = 500; i < 1000; i++) {
            arrayList.get(i).start();
        }}).start();

        Thread.sleep(30000);
    }

多个线程并发执行UPDATE某个列 ,是线程安全的。后执行的UPDATE语句会阻塞,直到前面的UPDATE语句回滚或者是提交。

并发修改可能出现脏数据的操作是,先读取数据到内存,在内存中进行更新,然后回写到数据库,此时可能导致覆盖了其他线程提交的数据。这种情况一般都是使用乐观锁,添加版本号的问题来解决。

“ 多个线程并发执行UPDATE 某个列 ,是线程安全的”,这是为啥没找到具体原因,我看有人说是因为MySQL的原子性保证的,大佬是怎么理解的呢?

MYSQL的UPDATE语句,是串行执行的,第一个线程执行UPDATE的时候,会把数据加锁。其他线程对加锁的数据再次执行UPDATE会被阻塞,直到第上一个个线程把事务提交,回滚。

UPDATE的时候,如果where条件命中索引,那么只会锁住索引列,如果不能命中索引,则是锁表。

于是像这种自增更新, 并非环境下修改,就是线程安全的

UPDATE user SET balance = balance + 1  WHERE id = 1

第1个线程执行update,就会把这条记录锁住,因为命中了索引列(id),那么只会锁住这条记录。其他线程修改这条记录的时候,就必须等待它提交后才会执行update,这个是串行执行。也就不会有线程安全的问题了。

2 个赞

谢谢大佬,收货颇多

1 个赞