讨厌什么变成什么 2019-06-27
认为每次获取数据的时候数据一定会被人修改,所以它在获取数据的时候会把操作的数据给锁住,这样一来就只有它自己能够操作,其他人都堵塞在那里。
认为每次获取数据的时候数据不会被别人修改,所以获取数据的时候并没有锁住整个数据,但是在更新的时候它会去判断一下要更新的数据有没有被别人修改过,例如更新前查询该数据的版本号,更新的时候看看该版本号有没有被人修改过,如果被人修改过了,那就不会去更新。
悲观锁:
因为悲观锁会锁住数据,让其他人都等待,所以当一个系统并发量不大,而且可以接收一定延迟的时候可以选择悲观锁。
乐观锁:
因为乐观锁会在更新前去查数据,所以比较适合读多少写的场景,因为写操作多的话会造成大量的查询操作,给系统带来压力。例如SVN、Git等版本控制管理器就是应用的乐观锁,当你提交数据的时候对比下版本号,如果远程仓库的版本号和本地的不一样就表示有人已经提交过代码了,你需要先更新代码到本地处理一下版本冲突问题,不然是没有办法提交的。
CAS是Compare And Set的缩写,中文意思就是比较和操作,是一个非阻塞算法。它其实是一个CPU的指令,它会拿内存值和一个给定的值进行比较,如果相等的话就会把内存值更新为另一个给定的值。其实CAS就是使用一个乐观锁的机制。
从JDK1.5开始java.util.concurrent.atomic包中新增了一些原子类,AtomicInteger、AtomicLong等等,就是专门解决高并发下的同步问题。因为类似i++、++i的操作不是线程安全的,以前我们都会使用Synchronized关键字,但是现在我们直接使用这些原子类就可以解决线程安全的问题。下面用代码来看看有什么变化。
class Test1 { private volatile int count = 0; public synchronized void increment() { //加锁才能保证线程安全 count++; } public int getCount() { return count; } } class Test2 { private AtomicInteger count = new AtomicInteger(); //使用AtomicInteger之后,不加锁,也是线程安全的。 public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
下面这些是AtomicInteger提供的别的方法。
//获取当前的值 public final int get() //获取当前的值,并设置新的值 public final int getAndSet(int newValue) //获取当前的值,并自增 public final int getAndIncrement() //获取当前的值,并自减 public final int getAndDecrement() //获取当前的值,并加上预期的值 public final int getAndAdd(int delta)
我们从源码的角度看看AtomicInteger是怎么实现CAS机制的。unsafe是java提供的用来获取对象内存地址的类,作用是在更新操作时提供“比较并替换”的作用。valueOffset是记录value本身在内存的地址,value被声明为volatile是保证在更新操作时,当前线程可以拿到value最新的值。
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value;
比如incrementAndGet方法,是获取当前的值并自增。
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
我们进getAndAddInt方法看看,getIntVolatile和compareAndSwapInt都是本地方法,就是通过本地方法来实现CAS机制。确保不出现线程安全问题。
public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5; } public native int getIntVolatile(Object var1, long var2); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);