乐观锁

1. 什么是乐观锁:

乐观锁是假设在查数据的时候数据不会被其他线程所修改,但是在更新数据的时候会校验数据有没有被修改过;它是一种比较交换机制,简称CAS(Compare And Swap)机制;

2. 实现方式:

在Java中乐观锁并没有确定的方法或者关键字,他只是一种处理流程、策略;一旦检测到有冲突产生,例如版本号或者最后更新时间不一致,它就会进行重试,直到没有冲突为止;在JAVA1.5以后,JDK官方提供了大量的原子类,这些类的内部都是基于CAS机制的,也就是使用了乐观锁;

3. 实现过程:

# AtomicInteger是一个原子类。
# 我们在调用i++的地方改成了i.incrementAndGet(),
# incrementAndGet()方法采用了CAS机制,也就是说使用了乐观锁
public class Test {
    private AtomicInteger i = new AtomicInteger(0);
    public static void main(String[] args) {
        Test test = new Test();
        ExecutorService es = Executors.newFixedThreadPool(50);
        CountDownLatch cdl = new CountDownLatch(5000);
        for (int i = 0;i < 5000; i++){
            es.execute(()->{
                test.i.incrementAndGet();
                cdl.countDown();
            });
        }
        es.shutdown();
        try {
            cdl.await();
            System.out.println("执行完成后,i="+test.i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 在检索数据时,将数据的版本号(version)或者最后更新时间一起检索出来
  • 操作员更改数据后,点击保存,在数据库执行update操作
  • 执行更新操作时,用先前检索出来的数据的版本号或者最后更新时间验证数据是否被其他线程更改过
  • 验证一致,修改成功
  • 验证不一致,提示数据已被修改

4. 总结:

乐观锁在读取数据时不做任何限制,在修改数据时会先对数据的版本进行比较,保证数据版本一致在进行更新;根据这个特点可以看出,乐观锁适用于读操作多,而写操作少的场景;

悲观锁

1. 什么是悲观锁

悲观锁是从读取数据时就给数据加上锁,直到数据更新完毕,才会释放锁;在这个期间只能有一个线程去操作,其他线程只能等待;

2. 实现方式:

在Java中,悲观锁可以使用synchronized关键字或者ReentrantLock类来实现;

3. 实现过程

# synchronized 实现悲观锁
public class Test {
    private int i=0;
    public static void main(String[] args) {
        Test test = new Test();
        ExecutorService es = Executors.newFixedThreadPool(50);
        CountDownLatch cdl = new CountDownLatch(5000);
        for (int i = 0;i < 5000; i++){
            es.execute(()->{
                //悲观锁  开始
                synchronized (test){
                    test.i++;
                }
                //悲观锁  结束
                cdl.countDown();
            });
        }
        es.shutdown();
        try {
            cdl.await();
            System.out.println("执行完成后,i="+test.i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

# ReentrantLock 实现悲观锁
public class Test {
    //添加了ReentrantLock锁
    Lock lock = new ReentrantLock();
    private int i=0;
    public static void main(String[] args) {
        Test test = new Test();
        ExecutorService es = Executors.newFixedThreadPool(50);
        CountDownLatch cdl = new CountDownLatch(5000);
        for (int i = 0;i < 5000; i++){
            es.execute(()->{
                //修改部分  开始
                test.lock.lock();
                test.i++;
                test.lock.unlock();
                //修改部分  结束
                cdl.countDown();
            });
        }
        es.shutdown();
        try {
            cdl.await();
            System.out.println("执行完成后,i="+test.i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. 总结

悲观锁从读取数据的时候就加上锁,更新数据的时候保证只有一个线程在执行更新操作,没有像乐观锁那样进行版本比较;所以悲观锁适用于读操作相对少,写操作相对多的场景;

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

那一年,我也变成了光!!