大连网站快速排名提升,网站上怎么做支付接口,网站建设的进度,盛大印刷公司网页设计乐观锁对应于生活中乐观的人总是想着事情往好的方向发展#xff0c;悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。 一、引入概念
1、悲观锁
总是假设最坏的情况#xff0c;每次去拿数据的时候都认为别人会修改#xff0c;所以每次在拿数据的时候都会上锁#… 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。 一、引入概念
1、悲观锁
总是假设最坏的情况每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候都会上锁这样别人想拿这个数据就会阻塞直到它拿到锁共享资源每次只给一个线程使用其它线程阻塞用完后再把资源转让给其它线程。传统的关系型数据库里边就用到了很多这种锁机制比如行锁表锁等读锁写锁等都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
2、乐观锁
总是假设最好的情况每次去拿数据的时候都认为别人不会修改所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去更新这个数据可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型这样可以提高吞吐量像数据库提供的类似于write_condition机制其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
3、两种锁的使用场景
从上面对两种锁的介绍我们知道两种锁各有优缺点不可认为一种好于另一种像乐观锁适用于写比较少的情况下多读场景即冲突真的很少发生的时候这样可以省去了锁的开销加大了系统的整个吞吐量。但如果是多写的情况一般会经常产生冲突这就会导致上层应用会不断的进行retry这样反倒是降低了性能所以一般多写的场景下用悲观锁就比较合适。
二、乐观锁的实现方式——CAS算法
1、CAS算法
什么是CAS
CAS全称为Compare And Swap即比较并交换其算法公式如下 函数公式CAS(V,E,N)V表示要更新的变量E表示预期值N表示新值 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpYccXsQ-1600049416983)(https://pics3.baidu.com/feed/810a19d8bc3eb135116a411f9e3739d6fc1f4497.jpeg?tokenc57722e5bdc2b5d2e02a24affb2e2ac9s6AAC3C6203AEC5EF5CF530CE000080B1)]CAS
如果V值等于E值则将V的值设为N。若V值和E值不同则说明已经有其他线程做了更新则当前线程什么都不做。通俗的理解就是CAS操作需要我们提供一个期望值当期望值与当前线程的变量值相同时说明还没线程修改该值当前线程可以进行修改也就是执行CAS操作但如果期望值与当前线程不符则说明该值已被其他线程修改此时不执行更新操作但可以选择重新读取该变量再尝试再次修改该变量也可以放弃操作。
2、“ABA问题”与“版本号机制”
CAS 会导致“ABA 问题”。CAS 算法实现一个重要前提需要取出内存中某时刻的数据而在下时刻比较并替换那么在这个时间差类会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出 A这时候另一个线程 two 也从内存中取出 A并且two 进行了一些操作变成了 B然后 two 又将 V 位置的数据变成 A这时候线程 one 进行 CAS 操作发现内存中仍然是 A然后 one 操作成功。尽管线程 one 的 CAS 操作成功但是不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题乐观锁每次在执行数据的修改操作时都会带上一个版本号一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行1 操作否则就执行失败。因为每次操作的版本号都会随之增加所以不会出现 ABA 问题因为版本号只会增加不会减少。
3、Java中的CAS
JDK5增加java.util.concurrent包其中很多类使用了CAS操作。这些CAS操作基于Unsafe类中的native方法实现
//第一个参数o为给定对象offset为对象内存的偏移量通过这个偏移量迅速定位字段并设置或获取该字段的值
//expected表示期望值x表示要设置的值下面3个方法都通过CAS原子指令执行操作
//设置成功返回true否则返回false。
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);由于CAS作用的对象在主存里而不是在线程的高速缓存里CAS操作在Java中需要配合volatile使用。
Java中的CAS主要包含以下几个问题
ABA问题即变量V初次读时是A值被赋值时也是A值但期间变量被赋值成B值,CAS会误认为他从没被修改过。AtomicStampedReference和AtomicMarckableReference类提供了监测ABA问题的能力其中的compareAndSet方法首先检查当前引用是否等于预期引用并且当前标志等于预期标志全部相等则以原子方式将该引用和该标志的值设置为给定的更新值。循环开销自旋CAS长时间不成功会给CPU带来非常大的执行开销。若JVM能支持pause命令效率有一定提升。因为pause命令一方面可以延迟流水线执行命令使CPU不会消耗过多的执行资源另一方面可以避免退出循环时由内存顺序冲突引起的CPU流水线被冲突从而提高CPU的执行效率。只能保证一个共享变量的原子操作当操作涉及跨多个共享变量时CAS无效。可用AtomicReference封装多个字段来保证引用对象之间的原子性。
三、悲观锁——synchronized
1、synchronized
synchronized是Java中的关键字是一种同步锁。可修饰实例方法静态方法代码块。
修饰实例方法:对当前实例加锁进入同步代码前要获得当前实例的锁修饰静态方法:对当前类对象加锁进入同步代码前要获得当前类对象的锁修饰代码块:指定加锁对象对给定对象加锁进入同步代码库前要获得给定对象的锁。
2、CAS与synchronized的使用情景
简单的来说CAS适用于写比较少的情况下多读场景冲突一般较少synchronized适用于写比较多的情况下多写场景冲突一般较多对于资源竞争较少线程冲突较轻的情况使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源而CAS基于硬件实现不需要进入内核不需要切换线程操作自旋几率较少因此可以获得更高的性能。对于资源竞争严重线程冲突严重的情况CAS自旋的概率会比较大从而浪费更多的CPU资源效率低于synchronized。 补充 Java并发编程这个领域中synchronized关键字一直都是元老级的角色很久之前很多人都会称它为 “重量级锁” 。但是在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 Lock-Free 的队列基本思路是 自旋后阻塞竞争切换后继续竞争锁稍微牺牲了公平性但获得了高吞吐量。在线程冲突较少的情况下可以获得和CAS类似的性能而线程冲突严重的情况下性能远高于CAS。