前言:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

为什么需要锁(并发控制)?

Hibernate的悲观锁加锁模式有:

1. LockMode.NONE:无锁机制

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

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

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

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


二、乐观锁大多是基于数据版本(Version)记录机制实现。Hibernate在其数据访问引擎中内置了Hibernate乐观锁实现,可以通过class描述符的optimistic-lock属性结合version描述符指定。optimistic-lock属性有如下可选取值:

1. none:无乐观锁

2. version:通过版本机制实现乐观锁

3. dirty:通过检查发生变动过的属性实现乐观锁

4. all:通过检查所有属性实现乐观锁

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。

典型的冲突有:

l 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。

2 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。

为了解决这些并发带来的问题。 需要用到锁机制



乐观锁:

   乐观锁就是 update set where 把所有的锁定都延迟到即将执行更新之前才做。

我们乐观的认为数据跟之前查的时候没有变化,提交操作时检查是否违反数据完整性,执行更新失败的可能性很大,因为可能在你查询和更新之间数据已经被改过了。

   乐观锁大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。


悲观锁:

   悲观锁就是 select from where for update nowait; 手动锁定这个结果集,不允许其他会话更新。

在视图更新之前我们就把数据锁住了,因为我们很悲观,无法确定这一行能不能保持未改变。

for update是啥意思啊?

select a from table where xx=xx for update;如果要修改这个数据之前先运行这个

别人就不能再改这个数据了,之后再运行update 就能确保改的数据一定是别人没动过的

优点:数据的一致性保持得很好,缺点:不适合多个用户并发访问。当一个锁住的资源不被释放掉的时候,这个资源永远不会被其他用户进行修改,容易造成无限期的等待。

   悲观锁需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL  Server  将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

SqlServer中使用


Begin Tran

select top 1 @TrainNo=T_NO

        from Train_ticket   with (UPDLOCK)   where S_Flag=0


     update Train_ticket

        set T_Name=user,

            T_Time=getdate(),

            S_Flag=1

        where T_NO=@TrainNo

commit

我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

结论

在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.


死锁

疑问:我们java程序经常会莫名的卡死,我们通过重启便可以解决,请问下这个是什么导致的?

死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。


导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。


由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。


//代码一

class Deadlocker {

intfield_1;

private Object lock_1 = new int[1];

intfield_2;

private Object lock_2 = new int[1];


public void method1(int value) {

“synchronized” (lock_1) {

 “synchronized” (lock_2) {

  field_1 = 0; field_2 = 0;

  }

 }

}


public void method2(int value) {

“synchronized” (lock_2) {

 “synchronized” (lock_1) {

  field_1 = 0; field_2 = 0;

  }

 }

}

}