乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据:如果别人修改了数据则放弃操作,否则执行操作。
悲观锁:悲观锁在操作数据时比较悲观,认为别人会同时修改数据。因此操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据。
注意:乐观锁本质没有锁,因此使用它可以提高代码执行效率,不会阻塞,不会等待,会重试。
二,锁的实例
乐观锁:1.在Java中java.util.concurrent.atomic包下面的原子变量类(采用CAS机制)。
2.版本号机制。
悲观锁:悲观锁的实现方式就是加锁,通过给代码块加锁,或数据加锁。
1.给数据加锁--传统的关系型数据库中的锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
2.给代码块加锁--比如Java里面的同步原语synchronized关键字的实现也是悲观锁。
1,CAS机制
CAS操作方式:即compare and swap 或者compare and set ,涉及到三个操作数,数据所在的内存地址(V),预期值(A),新值(B)。当需要更新时,判断当前内存地址的值与之前取到的值是否相等,若相等,则用新值更新,若不等则重试,一般情况下是一个自旋操作,即不断的重试。
CAS 操作中包含三个操作数 —— 需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的现值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,否则处理器将重复匹配。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)
CAS是乐观锁技术,当多个线程同时尝试使用CAS更新同一个变量时,只有其中一个线程能成功更新变量的值,其它线程均会失败,但失败的线程并不会被挂起,只是被告知这次竞争失败,并且允许失败的线程再次尝试,当然也允许失败的线程放弃操作。这里再强调一下,乐观锁是一种思想,CAS是这种思想的一种实现方式。虽然与加锁相比,CAS比较交换会使程序看起来更加复杂一些。但由于其非阻塞性,对死锁问题天生免疫,更重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销,因此,它要比基于锁方式的实现拥有更优越的性能。
在Java1.5之前是靠synchronized关键字保证同步的,这是一种独占锁,也是一种悲观锁,是一个重量级的操作,因为加锁需要消耗额外的资源,还因为线程状态的切换会涉及操作系统核心态和用户态的转换;所以在1.5之后Java增加了并发包Java.util.concurrent.*(j.u.c)。J.U.C就是建立在CAS之上的,相对于对于 synchronized 这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。不过随着JVM对锁进行的一系列优化(如自旋锁、轻量级锁、锁粗化等),synchronized的性能表现也已经越来越好。
现在来介绍AtomicInteger。AtomicInteger是java.util.concurrent.atomic包提供的原子类,利用CPU提供的CAS操作来保证原子性;除了AtomicInteger外,还有AtomicBoolean、AtomicLong、AtomicReference等众多原子类。原子操作类大致可以分为4类:原子更新基本类型,原子更新数组类型,原子更新引用类型,原子更新属性类型。这些原子类中都是用了无锁的概念,有的地方直接使用了CAS机制。
下面以 java.util.concurrent 中的 AtomicInteger 为例,看一下在不使用锁的情况下是如何保证线程安全的。主要理解 getAndIncrement 方法,该方法的作用相当于 ++i 操作。
public class AtomicInteger extends Number implements java.io.Serializable { //在没有锁的机制下,字段value要借助volatile原语,保证线程间的数据是可见性。 private volatile int value; //Unsafe用于实现对底层资源的访问 private static final Unsafe unsafe = Unsafe.getUnsafe(); //valueOffset是value在内存中的偏移量 private static final long valueOffset; //通过Unsafe获得valueOffset static { try { valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final int getAndIncrement() {//相当于++i操作 for (;;) { int current = get();//获取值 int next = current + 1;//+1操作 if (compareAndSet(current, next))//current是预期值,即从主存中取来还未操作过的值,next更新后的值 return current; } } }