一、锁的基本简介

1.1,为什么需要锁

首先,锁的概念产生,主要是为了解决并发性的问题。什么是并发性问题呢,比如:

Angel现在银行有个账号,里面有存款1000块。现在,Angel的账户,在两个地方分别执行操作。首先,Angel妈妈拿着主卡在四川给Angel账户存钱,读取出Angel的余额是1000块。其次,Angel拿着副卡在廊坊从账户里取钱,读取出angel 的余额是1000块。这时候,angel取了200块钱,余额变为1000-200=800块。可是angel妈妈执行存钱1000块操作,Angel余额变为1000+1000=2000(Angel妈妈最初读取出的余额为1000)这时候,Angel的余额就出现了问题,按照正常逻辑,账户余额为1000-200+1000=1800块。这个问题,就叫做更新丢失。Angel的账户,丢失了取款200的更新。为了解决这一个问题,我们需要引入锁的概念

1.2,悲观锁

悲观锁:通常是有数据库机制实现的,在整个过程中把数据锁住(查询),只要事务不释放(提交 / 回滚),那么任何用户都不能查看或修改。

正如上面的例子,Angel妈妈在四川,读取出了Angel的余额,那么angel的账户就被锁定了。angel就不能在廊坊对其账户进行操作,只有等到angel妈妈对这个账户的更新结束,也就是正常更新余额1000+1000=2000的业务结束,angel才能在廊坊执行取款操作。那么,悲观锁对外界的修改持保守态度,有效的保证了数据的一致性。但是,优点:它不适合多个用户并发访问。当一个锁住的资源不被释放掉的时候,这个资源永远不会被其他用户进行修改,容易造成无限期的等待,也就是等待超时。(想象一下,angel妈妈一直没有执行完存钱操作,angel取钱的道路该是多么的艰辛。。。或者说angel一直没有执行完取钱操作,angel妈妈的存钱道路该有多么心酸。。。)

那么,怎样能保证数据的一致性,又不会导致无期限的等待呢?

1.3,乐观锁

乐观锁,从本质上来说并不是一种锁,它大多数的使用是采用数据版本的方式(version)实现,一般在数据库中加入一个version字段,在读取数据的时候将version读取出来,在保存数据的时候判断version的值是否小于数据库中的version值,如果小于不予更新,否则给予更新。

如果运用乐观锁的实现机制去解决Angel的取款问题,则会发生什么呢?首先,angel妈妈在四川读出angel的余额1000,和其数据的版本号1;同时angel在廊坊也读取出了余额1000,和版本号1。这时候angel执行了取款操作200,更新余额为1000-200=800,同时将数据版本号更新为2。这时候angel妈妈执行存钱操作,而版本号1<2,所以不予以执行1000+1000=2000的更新操作。若是要存款,只能再次执行业务流程,这样,保证了数据的一致性。

1.4,Hibernate的加锁模式

A: LockMode.NONE : 无锁机制。

B:LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会自动获取。 

C:LockMode.READ : Hibernate 在读取记录的时候会自动获取。

以上这三种锁机制一般由 Hibernate 内部使用,如 Hibernate 为了保证 Update过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE 锁。

D: LockMode.UPGRADE :利用数据库的 for update 子句加锁。

E: LockMode. UPGRADE_NOWAIT : Oracle 的特定实现,利用 Oracle 的 for update nowait 子句实现加锁。

二、实例分析Hibernate的锁机制

2.1,乐观锁

<span style="font-family:KaiTi_GB2312;font-size:18px;"><?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.angel.hibernate.Inventory" table="t_inventory" <span style="color:#ff0000;">optimistic-lock="version"</span>>
<id name="itemNo">
<generator class="assigned"/>
</id>
<version name="version"/>
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping></span>

测试类:

<span style="font-family:KaiTi_GB2312;font-size:18px;">package test.com.angel.hibernate;

import junit.framework.TestCase;

import org.hibernate.Session;

import com.angel.hibernate.HibernateUtils;
import com.angel.hibernate.Inventory; public class OptimisticLockingTest extends TestCase { public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002");
System.out.println("opt1-->itemNo=" + inv.getItemNo());
System.out.println("opt1-->itemName=" + inv.getItemName());
System.out.println("opt1-->version=" + inv.getVersion());
System.out.println("opt1-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002");
System.out.println("opt2-->itemNo=" + inv.getItemNo());
System.out.println("opt2-->itemName=" + inv.getItemName());
System.out.println("opt2-->version=" + inv.getVersion());
System.out.println("opt2-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() + 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} }
</span>

当我们在方法1读取数据结束,但是未提交事务之前,紧接着执行方法2的时候,由于version字段的值被更改,所以会导致方法1执行不通过,从而保证了数据的一致性。

2.2,悲观锁

<span style="font-family:KaiTi_GB2312;font-size:18px;">package test.com.angel.hibernate;

import junit.framework.TestCase;

import org.hibernate.LockMode;
import org.hibernate.Session; import com.angel.hibernate.HibernateUtils;
import com.angel.hibernate.Inventory; public class OptimisticLockingTest extends TestCase { public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002",LockMode.UPGRADE);
System.out.println("opt1-->itemNo=" + inv.getItemNo());
System.out.println("opt1-->itemName=" + inv.getItemName());
System.out.println("opt1-->version=" + inv.getVersion());
System.out.println("opt1-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002",LockMode.UPGRADE);
System.out.println("opt2-->itemNo=" + inv.getItemNo());
System.out.println("opt2-->itemName=" + inv.getItemName());
System.out.println("opt2-->version=" + inv.getVersion());
System.out.println("opt2-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() + 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} }
</span>

当我们在方法1读取数据结束,但是未提交事务之前,紧接着执行方法2的时候,由于添加悲观锁的缘故,方法2无法执行,只有当方法1进行了提交,方法2才能继续执行。

三、总结

加锁可以有效的解决并发问题,但是,这也得根据应用程序的具体情况而定。如果开发 的应用程序都是单人操作,那么根本就不必引入锁的概念。在这里,顺便总结一下数据中锁的基本分类:

共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。 

更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。 

排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。

【Hibernate 9】悲观锁和乐观锁的更多相关文章

  1. Hibernate解决高并发问题之:悲观锁 VS 乐观锁

    高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...

  2. 025 hibernate悲观锁、乐观锁

    Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级 ...

  3. Hibernate 再接触 悲观锁和乐观锁

    为什么取1248 二进制 CRUD 移位效率高 在并发和效率选择一个平衡点 一般不会考虑幻读 因为我们不会再一个事务里查询两次,(只能设置为seralizable) 悲观锁和乐观锁的前提是read-u ...

  4. Hibernate的悲观锁和乐观锁

    前一篇博客我们从数据库角度分析,锁可以分为三种,分别为共享锁,独占锁和更新锁.我们从程序的角度来看锁可以分为两种类型,悲观锁和乐观锁,Hibernate提供对这两种锁 的支持,我们来了解一下Hiber ...

  5. mysql-mysql悲观锁和乐观锁

    1.mysql的四种事务隔离级别 I. 对于同时运行多个事务,当这些事务访问数据库中的相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题. (1)脏读: 对于两个事物 T1, T2, T1 ...

  6. Oracle数据库悲观锁与乐观锁详解

    数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...

  7. MySQL学习笔记(四)悲观锁与乐观锁

    恼骚 最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的 for update 锁 在TP5直接通过lock(true),用于数据库的锁机制 Db::name('p ...

  8. 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  9. Oracle的悲观锁和乐观锁

    为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫 ...

  10. (转载)Oracle的悲观锁和乐观锁

    为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫 ...

随机推荐

  1. 黄聪:wordpress源码解析-数据库表结构(转)

    如果是一个普通的用户,不需要了解wordpress数据库的结构.但是,如果你正在写一个插件,你应该会对wordpress如何处理它的数据和关系感兴趣.如果你已经尝试使用已经存在的wordpress a ...

  2. Top 6 Programming Languages for Mobile App Development

    Mobile application development industry in the last five years have multiplied in leaps and bounds, ...

  3. smartgit document Rebase

    The Rebase command allows you to apply commits from one branch to another. Rebase can be viewed as m ...

  4. php 共享内存

    共享内存主要用于进程间通信 php中的共享内存有两套扩展可以实现 1.shmop  编译时需要开启 --enable-shmop 参数 实例: $shm_key = ftok(__FILE__, 't ...

  5. Mac 10.10下安装MySQL5.6.21提示安装失败

    只要要在安装的第三步在自定里不要选Startup item就可以了

  6. SPR EAD NET 6

    SPR EAD_NET6 下载地址 http://www.gcpowertools.com.cn/downloads/trial/Spread.NET/EN_SPREAD_NET6_SETUP_RA_ ...

  7. 全文检索引擎Solr系列—–全文检索基本原理

    场景:小时候我们都使用过新华字典,妈妈叫你翻开第38页,找到“坑爹”所在的位置,此时你会怎么查呢?毫无疑问,你的眼睛会从38页的第一个字开始从头至尾地扫描,直到找到“坑爹”二字为止.这种搜索方法叫做顺 ...

  8. 离线安装PM2

    因为要部署应用的机器没有公网连接,所以直接npm install pm2是不可能了, 简单记录一下怎么离线安装pm2 首先,找一台可以在线安装pm2的机器 执行 npm install pm2 -g ...

  9. 02.lib-v1.js

    /* Date: 2014-07-29 4:06:07 [PM] */ function StringBuilder() { this.strings = new Array, this.length ...

  10. C++学习32 重载new和delete运算符

    内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...