案例说明:火车票售票系统
同时启动多个线程模拟买票
窗口首先查询出还剩余多少张票,判断是否足够,再进行更新操作
所以是一条select语句,一条update语句现在出现的问题:
第一个人买首先查询剩余20张,买了2张,update为18张
第二个人在update之前就读出了,所以他读出的剩余票数也是20张,买了2张,update为18张结果买了4张,确只减少了2张请问如何解决这个问题呢?
或者遇到这种问题是怎么处理的(java)
不用synchronized锁方法

解决方案 »

  1.   

    这种把总张数这样放在表中是不对的。你应该只有个总票数。剩余票数应该用总数减去已售出数。剩余数不是表的属性,只是一个衍生的统计值。
    非要保存这个字段,你可以通过在售出明细表上的trigger来实现同步。
      

  2.   

    现在用select...for update解决了
    select的时候加锁了
      

  3.   


    求解?1select...
    2update...用join
      

  4.   

    这种方式可能会有问题。
    如果不是存储过程,for update之后,程序当掉了,没有commit,则其他针对于该表的操作都会idle直到该连接被重置。
      

  5.   


    那该如何处理  用for update(no wait)子句
      

  6.   


    啥数据库?oracle的存储过程是原子的,如果是oracle就用存储过程来做吧
    mysql的存储过程不是原子的,需要java调用的时候setAutoCommit(false)开启事务才能保证整个过程的原子性。
    sqlserver及db2未用过他们的存储过程,不了解。
    嫌麻烦,则也可以按照2楼的方式利用一些字段来处理。
      

  7.   

    其实你用FOR UPDATE虽然能解决问题,也可以通过事务的序列化完成,但是他们都会出现异常,用异常去控制流程一般是不推荐的,我可以等一会,如果不行再说,所以我一般应对类似的问题会做一下操作:方法一:
    在JAVA程序中调用存储过程的时候,有一个共享对象,Boolean类型,代表该对象是否正在被调用,synchronized控制对对象的访问,顺序调用该过程,或者用易失变量判定,若发现被调用,此时设置为wait()指导当前调用者将它notify操作。方法二:
    判定不要直接使用for update,我习惯这样使用:
    SELECT count(1) into COUNTER FROM test WHERE id=<关键字> FOR UPDATE NO WAIT SKIP LOCKED;
    此时你可以获取COUNTER的值是否为0,如果为0代表正在操作,这个怎么操作就你自己决定了,让记得ROLLBACK和COMMIT。。
    付:FOR UPDATE NO WAIT SKIP LOCKED该SQL或根据查询条件并跳过被锁住的行,所以如果被锁住了,就不会得到结果,当然COUNTER就为0了,如果得到值,说明你要操作的数据还没有被加锁,此时可以继续操作那条数据了,因为这个线程获取到了这个锁还有就是UPDATE的时候应当以当前值进行修改,如
    UPDATE test
      set num = num - <卖掉的数量>
      WHERE id = <关键字>;
      

  8.   

    用select……for update设置对update操作的悲观锁
      

  9.   

    您好,根据我对数据库的了解,可能你的担心是不需要的.因为按照数据库“读一致性”的机制,当用户检索数据时,即使有用户执行DML(增,删,改,查)语句未提交(指执行了sql语句但未commit),用户检索的数据也是最新的数据,也就是已执行后的数据,因为用户检索的是从undo表空间中取出的数据(即使还没写入数据文件)。您可以查下数据库的undo原理和读一致性,并实践测试我的说法,希望能帮到你。
      

  10.   


    据我了解 用户执行DML(增,删,改,查)语句未提交(指执行了sql语句但未commit),用户检索的数据并不是最新的。而sql server才可以启用脏读 set transaction read uncommit时才是你说的这样 而oracle不行