@[toc]
锁都分类
什么是悲观锁,乐观锁
乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
如图所示 ,synchronized 就是悲观锁都一个实现思路,如下代码,
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁
private Object object =new Object();
public void sale(){
synchronized (object){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
}
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
以 java.util.concurrent 中的 AtomicInteger 为例
AtomicInteger 内部使用 cas无锁机制,不会进行加锁, 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
package com.dimple.test;
import java.util.concurrent.atomic.AtomicInteger;
public class Test5 {
public static void main(String[] args) {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"窗口一");
Thread t2 = new Thread(thread,"窗口二");
t1.start();
t2.start();
}
}
class TestDemo implements Runnable{
//共享的火车票变量
private static AtomicInteger atomic = new AtomicInteger(100);
//重寫run方法
@Override
public void run() {
while (atomic.get() > 0){
try {
//休眠一下 方便出现并发问题
Thread.sleep(50);
}catch (Exception e){
e.getMessage();
}
sale();
}
}
//卖票
public void sale(){
if(atomic.get() > 0){
Integer count= 100 - atomic.getAndDecrement() + 1; //使用底层方法getAndDecrement() 自-1;
System.out.println(Thread.currentThread().getName()+ "," + count);//获取当前值
}
}
}
经典案例
- git 版本控制工具 典型都乐观锁实现思路,当我们往远程仓库 push的时候 ,git 会检查我们的版本是否落后于版本库的版本,如果落后,push就会报错,如果一致,就提交成功
- git 不适用于悲观锁,否则 公司倒闭
开销对比
悲观锁的开销肯定要大于乐观锁,但是特点一劳永逸,乐观锁 始终无锁机制,比对版本号,性能要优于 悲观锁,