首页编程java编程java锁机制有什么用 如何理解hibernate当中的锁机制

java锁机制有什么用 如何理解hibernate当中的锁机制

编程之家2023-10-1193次浏览

很多朋友对于java锁机制有什么用和如何理解hibernate当中的锁机制不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

java锁机制有什么用 如何理解hibernate当中的锁机制

java中文件加锁机制是怎么实现的。

Java中文件加锁机制如下:

在对文件操作过程中,有时候需要对文件进行加锁操作,防止其他线程访问该文件。对文件的加锁方法有两种:

第一种方法:使用RandomAccessFile类操作文件。

java锁机制有什么用 如何理解hibernate当中的锁机制

在java.io.RandomAccessFile类的open方法,提供了参数实现独占的方式打开文件:

RandomAccessFile raf= new RandomAccessFile(file,"rws");

其中的“rws”参数,rw代表读取和写入,s代表了同步方式,也就是同步锁。这种方式打开的文件,就是独占方式的。

java锁机制有什么用 如何理解hibernate当中的锁机制

第二种方法:使用sun.nio.FileChannel对文件进行加锁。

代码:

RandomAccessFile raf= new RandomAccessFile("file.txt","rw");

FileChannel fc= raf.getChannel();

FileLock fl= fc.tryLock();

if(fl.isValid())

System.out.println("You have got the file lock.");

以上是通过RandomAccessFile来获得文件锁的,方法如下:

代码:

FileOutputStream fos= new FileOutputStream("file.txt");

FileChannel fc= fos.getChannel();//获取FileChannel对象

FileLock fl= fc.tryLock();//or fc.lock();

if(null!= fl)

System.out.println("You have got file lock.");

//TODO write content to file

//TODO write end, should release this lock

fl.release();//释放文件锁

fos.close;//关闭文件写操作

如果在读文件操作的时候,对文件进行加锁,操作过程如下:

FileChannel也可以从FileInputStream中直接获得,但是这种直接获得FileChannel的对象直接去操作FileLock会报异常NonWritableChannelException,需要自己去实现getChannel方法,代码如下:

private static FileChannel getChannel(FileInputStream fin, FileDescriptor fd){

FileChannel channel= null;

synchronized(fin){

channel= FileChannelImpl.open(fd, true, true, fin);

return channel;

}

}

其实,看FileInputStream时,发现getChannel方法与我们写的代码只有一个地方不同,即open方法的第三个参数不同,如果设置为false,就不能锁住文件了。缺省的getChannel方法,就是false,因此,不能锁住文件。

如何理解hibernate当中的锁机制

hibernate的锁

博客分类:

hibernate

hibernate悲观锁乐观锁

业务实现过程中,难免需要保证数据访问的排他性。如金融系统的日终结算中,我们会针对某个截止点的数据进行处理,在此同时,不希望在结算的这段时间里(几秒钟或者几个钟头),数据再有变动,不然我们的统计也无效了。这种时候,我们为了保证某些数据在某个操作中不被外界修改,就引入了我们hibernate的锁机制,一旦我们给目标上锁,其他程序则不能去修改。hibernate中锁是两种,悲观和乐观锁。、

悲观锁,自然,它指的是对数据被外界(包括本系统当前的其他事物,以及来自外部系统的事务处理)修改,保持保守态度,因此整个处理过程中,数据一致处于被锁定的状态【额....还真是够悲观的..】,悲观锁的实现,往往依靠数据提供的锁机制(也只有数据库层提供的锁机制,才能真正保证排他性,否则本系统中实现加锁机制,并不能一定保证外部系统不会修改数据)。

下面,这是一个典型的,依赖数据库实现的悲观锁的调用:

Sql代码

select* from account where name="Erica" for update

[sql] view plain copy

select* from account where name="Erica" for update

通过for update子句,sql锁定了account中,所以符合名字等于erica的记录。即在本次事物提交之前(事物提交时会释放事物过程中的锁),外界无法修改这些记录。hibernate的悲观锁,也是基于数据库的锁机制实现的,下面代码实现了对查询记录的加锁:

Sql代码

String hqlStr="from User user where uesr.name='Erica'";

Query query=session.createQuery(hqlStr);

query.setLockMode("user",LockMode.UPGRADE);//加锁

List userList=query.list();//获得数据

[sql] view plain copy

String hqlStr="from User user where uesr.name='Erica'";

Query query=session.createQuery(hqlStr);

query.setLockMode("user",LockMode.UPGRADE);//加锁

List userList=query.list();//获得数据

当运行后,我们通过show_sql可以看见,原生的sql语句仍然在最后面加上了for update,可以看出hibernate通过数据库的for update自己来实现了悲观锁的机制。

Hibernate的加锁模式则分为:LockMode.NONE:无锁 LockMode.WRITE:hibernate在insert和update记录的时候会自动获取 LockMode.READ: hibernate在读取数据的时候会自动获取。

以上的3中机制,一般由hibernate内部使用,如hibernate为了保证update过程中对象不被外界修改,会在save方法实现中自动为目标对象加上WRITE锁,这些都是hibernate内部数据的锁定机制,与数据库无关。

而LockMode.UPGRADE:利用数据的for update子句加锁, LockMode.UPGRADE_NOWAIT: oracle特定的实现,利用oracle的for update nowait子句实现加锁。

上面这两种所机制是我们比较常用的,依赖数据库的悲观锁机制。加锁方式一般有:Criteria.setLockMode; Query.setLockMode; Session.lock,注意,只有在查询开始前(也就是hibernate生成sql前)设定加锁,才会真正通过数据库的锁机制来加锁处理,否则,数据已经通过不包含for update的子句的select语句加载了,锁机制也无从谈起。

乐观锁,则采取更加宽松的机制加锁。悲观的大都靠数据库的for update实现,在保证了独占性外,性能会相对消耗较大。对于长事物而言,基本无法承受额样的开销。而乐观锁则是通过给数据库增加一个version字段,通过比较版本信息,从而实现加锁机制。下面我们打个比方,就知道了。

数据库的accout表中,记录着张三的账户上有100块钱,此时银行还未上班,张三也未存取或转账,这时,乐观锁情况下,这个100块是version=1的,而然在上班之后,银行操作员a,将账户查了出来,在进行扣除,他想着要扣多少钱才好,于是他边喝咖啡边想,这时候,操作员b也来了,查到了张三的账户也想扣他的钱,这时a想好了,先行扣了张三的50块,即100-50,然后愉快的提交了,此时张三账户的version字段被+1,成为version=2了。后来b了想好了,扣了它20块,即100-20,他也想提交,但是突然报出提示,不能提交,缘故是提交的版本必须大于记录的版本才能执行,哦,他这才知道原来张三的账户已经被修改过了,所以才被驳回。

从上面的例子可以看出,乐观锁避免了长事务中的数据加锁的开销,操作员a和b在操作时,都未加锁,从而大大提升了大并发量的系统的整体的性能表现。而hibernate在其数据访问引擎中内置了对乐观锁的实现。如果不考虑外部系统对数据库的更新操作,利用hibernate提供的透明化的乐观所机制将大大提升生产力。

比如,我们为之前几篇博文中的User表加上乐观锁,即添加optimistic-lock属性:

Xml代码

<hibernate-mapping>

<class name="com.entity.Uesr" table="User" dynamic-update="true" dynamic-insert="true" optimistic-lock="vsersion">

<SPAN style="WHITE-SPACE: pre"></SPAN><version column="version" name="version" type="java.lang.Integer"/>

......

</class>

</hibernate-mapping>

[xml] view plain copy

<hibernate-mapping>

<class name="com.entity.Uesr" table="User" dynamic-update="true" dynamic-insert="true" optimistic-lock="vsersion">

<span style="WHITE-SPACE: pre"></span><version column="version" name="version" type="java.lang.Integer"/>

......

</class>

</hibernate-mapping>

上面的optimistic-lock属性则可选为none(无乐观锁);version(通过版本机制实现乐观锁);dirty(通过检查发生变动的属性实现乐观锁); all(通过检查所有属性实现乐观锁),其中version方式是hibernate官方推荐的方式,所以我在上面的举例,也是通过version的方式。

下面,我们尝试去更新User的记录,代码如下:

Java代码

Criteria ct=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

List userList=ct.list();

User user=(User)userList.get(0);

Transaction tx=session.beginTransaction();

user.setUserType(1)';//更新UserType字段为1

tx.commit();

[java] view plain copy

Criteria ct=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

List userList=ct.list();

User user=(User)userList.get(0);

Transaction tx=session.beginTransaction();

user.setUserType(1)';//更新UserType字段为1

tx.commit();

每次去更新的时候,我们可以发现,数据库的version一直在加1。如果我们在tx.commit之前,再启动一个session去对Erica进行更新,以模拟并发更新的话,那么代码就是这样的:

Java代码

Session session=getSession();

Criteria ct=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

Session session2=getSession();

Criteria ct2=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

List list1=ct.list();

List list2=ct2.list();

User user1=(User)list1.get(0);

User user2=(User)list2.get(0);

Transaction tx1=session.beginTransaction();

Transaction tx2=session2.beginTransaction();

user2.setUserType(99);

tx2.commit();

user2.setUserType(1);

tx1.commit();

[java] view plain copy

Session session=getSession();

Criteria ct=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

Session session2=getSession();

Criteria ct2=session.createCriteria(User.class);

ct.add(Expression.eq("name","Erica"));

List list1=ct.list();

List list2=ct2.list();

User user1=(User)list1.get(0);

User user2=(User)list2.get(0);

Transaction tx1=session.beginTransaction();

Transaction tx2=session2.beginTransaction();

user2.setUserType(99);

tx2.commit();

user2.setUserType(1);

tx1.commit();

那么,这段代码执行时会在tx1.commit()处,抛出StaleObjectStateException异常,指出版本检查失败,通过这个异常,我们就可以进行相应的处理。

到这里,假使看的认真的话,我们大概可以知道下面这几点,第一,hibernate锁是用来做什么的一般用于哪些场合或者系统;第二,什么是悲观锁,乐观锁,它们的机制是怎么样的。第三,基础的一些悲观锁乐观锁的使用方法和实际能解决的问题。希望我说的这三点,大家都能有所苟同。

JAVA中,线程死锁是什么意思

一.什么是线程

在谈到线程死锁的时候,我们首先必须了解什么是Java线程。一个程序的进程会包含多个线程,一个线程就是运行在一个进程中的一个逻辑流。多线程允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。

线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信较进程简单。笔者的经验是编写多线程序,必须注意每个线程是否干扰了其他线程的工作。每个进程开始生命周期时都是单一线程,称为“主线程”,在某一时刻主线程会创建一个对等线程。如果主线程停滞则系统就会切换到其对等线程。和一个进程相关的线程此时会组成一个对等线程池,一个线程可以杀死其任意对等线程。

因为每个线程都能读写相同的共享数据。这样就带来了新的麻烦:由于数据共享会带来同步问题,进而会导致死锁的产生。

二.死锁的机制

由多线程带来的性能改善是以可靠性为代价的,主要是因为有可能产生线程死锁。死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。这里举一个通俗的例子:如在人行道上两个人迎面相遇,为了给对方让道,两人同时向一侧迈出一步,双方无法通过,又同时向另一侧迈出一步,这样还是无法通过。假设这种情况一直持续下去,这样就会发生死锁现象。

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

Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。下面笔者分析死锁的两个过程“上锁”和“锁死”。

(1)上锁

许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,就需要同步机制。因此大多数应用程序要求线程互相通信来同步它们的动作,在 Java程序中最简单实现同步的方法就是上锁。在 Java编程中,所有的对象都有锁。线程可以使用 synchronized关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。

为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。给共享变量上锁就使得 Java线程能够快速方便地通信和同步。某个线程若给一个对象上了锁,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,这些睡眠进程就会被唤醒并移到准备就绪队列中。

(2)锁死

如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源,系统中没有饿死和死锁的线程。当多个并发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

在编写多线程代码时,笔者认为死锁是最难处理的问题之一。因为死锁可能在最意想不到的地方发生,所以查找和修正它既费时又费力。例如,常见的例子如下面这段程序。

public int sumArrays(int[] a1, int[] a2)...{ int value= 0; int size= a1.length; if(size== a2.length)...{ synchronized(a1)...{//1 synchronized(a2)...{//2 for(int i=0; i<size; i++) value+= a1[i]+ a2[i];}}} return value;}

这段代码在求和操作中访问两个数组对象之前锁定了这两个数组对象。它形式简短,编写也适合所要执行的任务;但不幸的是,它有一个潜在的问题。这个问题就是它埋下了死锁的种子。

没有完结,请楼主看下面的网址。

关于java锁机制有什么用的内容到此结束,希望对大家有所帮助。

java 查询所有进程命令是什么意思?linux查看java进程命令java面试什么书好,java入门书籍推荐