ava多线程中的锁分类多种多样,其中有一种主要的分类方式就是乐观和悲观进行划分的。这篇文章主要介绍如何自己手写一个乐观锁代码。不过文章为了保证完整性,会从基础开始介绍。
一、乐观锁概念
说是写乐观锁的概念,但是通常乐观锁和悲观锁的概念都要一块写。对比着来才更有意义。
1、悲观锁概念
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞,直到它拿到锁。
比如 synchronized 就是一个 悲观锁 ,当一个方法使用了synchronized修饰时,其他的线程想要拿到这个方法就需要 等到别的线程释放 。
数据库里面也用到了这种悲观锁的机制。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。这样其他的线程就不能同步操作,必须要等到他释放才可以。
2、乐观锁概念
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,只在更新的时候会判断一下 在此期间 别人有没有去更新这个数据。
注意 “在此期间” 的含义是 拿到数据到更新数据 的这段时间。因为没有加锁,所以别的线程可能会更改。还有一点那就是乐观锁其实是不加锁的。
了解了概念之后,再看个例子:java中的 Atomic 包下的类就是使用了乐观锁机制。我们挑出来一个看看官方是如何实现的,然后按照这样的实现机制我们自己就可以实现。
3、乐观锁实现案例
java并发机制中主要有三个特性需要我们去考虑, 原子性、可见性和有序性 。AtomicInteger的作用就是为了保证原子性。就是用这个演示:
public class Test {
//一个变量a
private static volatile int a = 0;
public static void main(String[] args) {
Test test = new Test();
Thread[] threads = new Thread[5];
//定义5个线程,每个线程加10
for (int i = 0; i < 5; i++) {
threads[i] = new Thread(() -> {
try {
for (int j = 0; j < 10; j++) {
System.out.println(a++);
Thread.sleep(500);
}
} catch (Exception e) {
e.printStackTrace();
}
}