未知驱动学习-数据库中的悲观锁和乐观锁范例
第一次接触这个名词,悲观锁,不知道是谁起的这样的名字. 很想知道为什么叫做悲观锁, 自己在占用一个东西的时候不允许别人碰就是悲观了(⊙_⊙)?这可能是理智的一种选择. 当然因为学习和生活即是相同的又是不同的, 我们可以这样解释的.
悲观锁是对数据的冲突采取一种悲观的态度, 比如上自习占座, 假设一定会有人和我抢一个座位, 我在最开始就把这个座位的时候,我就把这过座位贴上我的标签, 不允许任何人使用. 而乐观锁是我认为不一定有人和我抢座, 只有当我真正的要用到这个座位的时候再去看看这个座位上是否有人. 这就相当于, 有些人在发生一些事的时候总会把事情想坏,有的人则会把事情向好的方面想.
当然在数据库中我们可以这样解释, 悲观锁对数据的冲突采取的是一种悲观的态度, 就是数据肯定会冲突, 所以在开始读数据的时候就把数据锁住, 而乐观锁就是认为数据一般不会起冲突, 所以在数据进行提交更新的时候, 才会对数据冲突进行检测, 如果冲突了, 则让用户返回错误的信息, 让用户决定如何去做.
哪种情况使用悲观锁,哪种情况使用乐观锁?
针对数据并发的可能性比较大, 我们用悲观锁. 如果并发的可能性比较小, 我们用乐观锁.
悲观锁实例:
例如我们对oracle中的一个表进行修改更新操作, 但是在修改的时候首先要从数据库中读取出来这条要修改的内容, 悲观锁是在读取这条数据的时候就把表中的这条记录锁定, 不允许其他的用户来读取.
这样,我们数据库中的查询语句:
String sql="select value from T_TABLE_ID where table_name=? for update";
因为我们在连接数据库,执行操作的时候, 数据库会自动的提交事务, 我们需要把读取和更新这条记录放在一个事务里, 所以我们就要手动的去控制, 让我们读取更新全部完成的时候再提交事务.
1 所以先把事务"开关"自动变手动 , 就像开车的时候, 我们有手动挡和自动挡一样~~~
所以我们在jsp的后台页面上数据库工具类中添加方法的代码:
自动提交变手动提交事务:
//自己封装的让事务变成非自动回滚的函数. . public static void beginTransaction(Connection conn) { try{ if(conn!=null) { if(conn.getAutoCommit()) { //手动提交. 如果是true,则变为false. conn.setAutoCommit(false); } } }catch(SQLException e){ } }
这里面有一个jdbc的参数, connection的方法setAutoCommit()方法, true: 表示sql命令提交(commit) 由驱动程序负责 false: sql 命令提交由应用程序负责, 程序必须调用commit方法或rollback方法.
提交事务:
//提交事务. public static void commitTransaction(Connection conn) { try{ if(conn!=null) { if(!conn.getAutoCommit()) { //手动提交. 如果是true,则变为false. conn.commit(); } } }catch(SQLException e){ } }
回滚事务:
//回滚事务. public static void rollbackTransaction(Connection conn) { try{ if(conn!=null) { if(!conn.getAutoCommit()) { //手动提交. 如果是true,则变为false. conn.rollback(); } } }catch(SQLException e){ } }
恢复我们的设置, 让事务由手动变为自动, 恢复到最开始的状态:
//恢复以前的状态,重置. public static void resetConnection(Connection conn) { try{ if(conn!=null) { if(conn.getAutoCommit()) { //手动提交. 如果是true,则变为false. conn.setAutoCommit(false); }else { conn.setAutoCommit(true); } } }catch(SQLException e){ } }
我们调用数据库操纵类的方法:
/** * 根据表名生成该表的序列. * @param tableName * @return 返回生成的序列. */ //public static synchronized int generate(String tableName) public static int generate(String tableName) { //用这样的sql语句,使用了数据库的悲观锁. //在查询的时候就可以用. String sql="select value from T_TABLE_ID where table_name=? for update"; Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; //定义一个value的变量. int value = 0; try { conn = DbUtil.getConnection(); //不回滚事务,我们手动控制来回滚事务,这样就会占用这个锁. DbUtil.beginTransaction(conn); pstmt = conn.prepareStatement(sql); pstmt.setString(1,tableName); rs = pstmt.executeQuery(); //如果根据表明找到不到相应的记录,则抛出异常. if(!rs.next()) { throw new RuntimeException(); } //取得当前表的索引值. value = rs.getInt("value"); //自加. value++; //更新表索引值.. modifyValueField(conn,tableName,value); //我们完全改完了,就提交事务. DbUtil.commitTransaction( conn); }catch(Exception e) { e.printStackTrace(); //回滚事务. DbUtil.rollbackTransaction(conn); //再此抛出异常,为了防止继续运行,返回最开始的value. throw new RuntimeException(); }finally { //从里向外依次关闭. DbUtil.close(rs); DbUtil.close(pstmt); //重置connection的状态. DbUtil.resetConnection(conn); DbUtil.close(conn); } return value; }
以上是悲观锁的实例, 而乐观锁与悲观锁是相对的, 不是在读取这条数据的时候加锁而是在更新这条数据的时候加锁. 就相当于两个管理员同时在修改一个公司职员的信息时, 悲观锁是不允许同时修改的, 而乐观锁, 他们可以同时修改, 并且以最先保存结果的人为最后的保存结果, 同时通知另一个管理员无法保存的事实.
抛开计算机的世界, 你喜欢哪种性格的人呢?悲观?乐观? 我想做人要乐观, 做事要悲观. 做事我们必须要把坏结果提前想到, 并且做出一些必要的措施, 您说呢?