java锁的释义
1 各种锁的释义
1.1 死锁
死锁是指两个线程同时占用两个资源,又在彼此等待对方释放锁资源
import java.util.concurrent.TimeUnit;
public class LockExample {
public static void main(String[] args) {
deadLock(); // 死锁
}
/**
* 死锁
*/
private static void deadLock() {
Object lock1 = new Object();
Object lock2 = new Object();
// 线程一拥有 lock1 试图获取 lock2
new Thread(() -> {
synchronized (lock1) {
System.out.println("获取 lock1 成功");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 试图获取锁 lock2
synchronized (lock2) {
System.out.println(Thread.currentThread().getName());
}
}
}).start();
// 线程二拥有 lock2 试图获取 lock1
new Thread(() -> {
synchronized (lock2) {
System.out.println("获取 lock2 成功");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 试图获取锁 lock1
synchronized (lock1) {
System.out.println(Thread.currentThread().getName());
}
}
}).start();
}
}
1.2 悲观锁
悲观锁指的是数据对外界的修改采取保守策略,它认为线程很容易会把数据修改掉,因此在整个数据被修改的过程中都会采取锁定状态,直到一个线程使用完,其他线程才可以继续使用。
可以看出被 synchronized 修饰的代码块,在执行之前先使用 monitorenter 指令加锁,然后在执行结束之后再使用 monitorexit 指令释放锁资源,在整个执行期间此代码都是锁定的状态,这就是典型悲观锁的实现流程。
1.3 乐观锁
乐观锁认为一般情况下数据在修改时不会出现冲突,所以在数据访问之前不会加锁,只是在数据提交更改时,才会对数据进行检测。
Java 中的乐观锁大部分都是通过 CAS(Compare And Swap,比较并交换)操作实现的,CAS 是一个多线程同步的原子指令,CAS 操作包含三个重要的信息,即内存位置、预期原值和新值。如果内存位置的值和预期的原值相等的话,那么就可以把该位置的值更新为新值,否则不做任何修改。
ABA问题
1.4 可重入锁
可重入锁也叫递归锁,指的是同一个线程,如果外面的函数拥有此锁之后,内层的函数也可以继续获取该锁。在 Java 语言中 ReentrantLock 和 synchronized 都是可重入锁。
可重入锁的实现原理,是在锁内部存储了一个线程标识,用于判断当前的锁属于哪个线程,并且锁的内部维护了一个计数器,当锁空闲时此计数器的值为 0,当被线程占用和重入时分别加 1,当锁被释放时计数器减 1,直到减到 0 时表示此锁为空闲状
1.5 共享锁和独占锁
只能被单线程持有的锁叫独占锁,可以被多线程持有的锁叫共享锁。
独占锁指的是在任何时候最多只能有一个线程持有该锁,比如 synchronized 就是独占锁,而 ReadWriteLock 读写锁允许同一时间内有多个线程进行读操作,它就属于共享锁。
独占锁可以理解为悲观锁,当每次访问资源时都要加上互斥锁,而共享锁可以理解为乐观锁,它放宽了加锁的条件,允许多线程同时访问该资源。