1、锁机制java
经常使用的锁机制有两种:算法
一、悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操做。悲观锁的实现,每每依靠底层提供的锁机制;悲观锁会致使其它全部须要锁的线程挂起,等待持有锁的线程释放锁。数据库
二、乐观锁:假设不会发生并发冲突,每次不加锁而是假设没有冲突而去完成某项操做,只在提交操做时检查是否违反数据完整性。若是由于冲突失败就重试,直到成功为止。乐观锁大可能是基于数据版本记录机制实现。为数据增长一个版本标识,好比在基于数据库表的版本解决方案中,通常是经过为数据库表增长一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,以后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,若是提交的数据版本号大于数据库表当前版本号,则予以更新,不然认为是过时数据。 多线程
乐观锁的缺点是不能解决脏读的问题。并发
在实际生产环境里边,若是并发量不大且不容许脏读,可使用悲观锁解决并发问题;但若是系统的并发很是大的话,悲观锁定会带来很是大的性能问题,因此咱们就要选择乐观锁定的方法.
锁机制存在如下问题:
(1)在多线程竞争下,加锁、释放锁会致使比较多的上下文切换和调度延时,引发性能问题。
(2)一个线程持有锁会致使其它全部须要此锁的线程挂起。
(3)若是一个优先级高的线程等待一个优先级低的线程释放锁会致使优先级倒置,引发性能风险。
性能
2、CAS 操做this
JDK 5以前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。java.util.concurrent(J.U.C)种提供的atomic包中的类,使用的是乐观锁,用到的机制就是CAS,CAS(Compare and Swap)有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。atom
现代的CPU提供了特殊的指令,容许算法执行读-修改-写操做,而无需惧怕其余线程同时修改变量,由于若是其余线程修改变量,那么CAS会检测它(并失败),算法能够对该操做从新计算。而 compareAndSet() 就用这些代替了锁定。spa
以AtomicInteger为例,研究在没有锁的状况下是如何作到数据正确性的。.net
字段value须要借助volatile原语,保证线程间的数据是可见的(共享的)。这样在获取变量的值的时候才能直接读取。而后来看看++i是怎么作到的。getAndIncrement采用了CAS操做,每次从内存中读取数据而后将此数据和+1后的结果进行CAS操做,若是成功就返回结果,不然重试直到成功为止。而compareAndSet利用JNI来完成CPU指令的操做。
总体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操做都是利用相似的特性完成的。
而整个J.U.C都是创建在CAS之上的,所以对于synchronized阻塞算法,J.U.C在性能上有了很大的提高,是非阻塞的。
转载:https://blog.csdn.net/heyutao007/article/details/19975665