1、锁的分类
可以从不同的维度对锁分类。
1.1、可重入锁和不可重入锁
可重入锁是指线程A获得该锁以后,在不释放锁的情况下线程A还可继续对锁加锁。比如下面就连续获取同一个锁而不会报错。
synchronized(Test.class) {
synchronized(Test.class) {
......
}
}
Java 中的synchronized
,ReentrantLock
等都是可重入锁。大部分锁都是可重入锁,不可重入锁较少。
不可重入锁则是指线程A获得该锁以后,在不释放锁的情况下线程A不可继续对锁加锁。不释放锁的情况下继续获取同一个锁会有异常。
1.2、乐观锁和悲观锁
悲观锁:获取不到锁资源时,会将当前线程挂起(进入BLOCKED或者WAITING),这里会涉及到到用户态和内核的切换,而这种切换是比较消耗资源的。
-
用户态:JVM可以自行执行的指令,不需要借助操作系统执行
-
内核态:JVM不可以自己执行,需要操作系统才可以执行
乐观锁:获取不到锁资源,可以再次让CPU调度,重新尝试获取锁资源。
Java中提供的synchronized,ReetrantLock, ReentrantReadWriteLock都是悲观锁。而CAS
操作则是乐观锁, Atomic
原子类就是基于CAS
实现的。
Java中提供的CAS操作,就是乐观锁的一种实现。
1.3、公平锁和非公平锁
Java中提供的synchronized
只能是非公平锁。
Java中提供的RLock
可以实现公平锁和非公平锁。
公平锁:线程A获取到锁资源,线程B没有拿到,线程B去排队,线程C来了,锁被A持有,同时线程B在排队,直接排到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源。
非公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,先尝试竞争一波。
-
拿到锁资源:开心,插队成功
-
没有拿到锁资源,依然要拍到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源。
1.4、互斥锁和共享锁
Java中提供的synchronized
、ReentrantLock
是互斥锁。
Java中提供的ReentrantReadWriteLock
,有互斥锁,也有共享锁。
互斥锁:同一时间点,只会有一个线程持有着当前互斥锁。
共享锁:同一时间点,当前共享锁可以被多个线程同时持有。
如果是读操作,那就可以设成共享锁;如果是写操作,那就设成互斥锁。从名字就能看出ReentrantReadWriteLock
可读可写。
4.2.2 Reentrant锁
Reentrant = re-entrant,该类型的锁可以连续多次加锁。比如线程1
可以想下面这样,在未释放锁前再加锁。
lock.lock();
lock.lock();
lock.lock();
lock.lock();
这时,线程1
就连续四次对lock
对象加锁。Reentrant中的实例方法getHoldCount()
就返回4,表示当前对象对lock
对象加了4层锁。其他的线程想要获取lock
对象锁的话,就要等线程1
四次lock.unLock()
之后才可以。