乐观锁和悲观锁--简记
乐观锁(Optimistic Locking):
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。
悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大的程度的独占性。
但随之而来的就是数据库性能的大量开销,特别是对于长事务而言。
乐观锁大多是基于数据版本记录机制实现。
数据版本:为数据增加一个版本标识,在局域数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
悲观锁(Pessimistic Lock):
具有强烈的独占和排他特性。
它指的是对数据被外界(包括本系统当前的其它事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,急事本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
乐观锁:假定不会发生并发冲突,只在提交操作时检查是否违反数据完整性。(不能解决脏读问题)
java中的乐观锁和悲观锁:
cpu是分时复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生晋城的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据,当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,再通过nitify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等待资源可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
但是,由于在进程挂起和恢复执行中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。
比如,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可坑立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。
乐观锁的核心思路急事,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。