公平锁与非公平锁
概述
- 公平锁:
线程按照申请锁的顺序获取锁(FIFO先入先出)
- 非公平锁:
多个线程获取锁的顺序不按照申请锁的顺序,会出现后申请的线程比先申请的线程先获取锁的情况。(抢占锁)
高并发情况下,有可能会造成优先级反转或者饥饿现象。
ReentrantLock
ReentrantLock默认就是非公平锁,可以在构造函数中设置为公平锁
// 非公平锁
Lock lock1 = new ReentrantLock();
// 公平锁
Lock lock2 = new ReentrantLock(true);
-
公平锁在JAVA中来说:
- 在并发环境中,每个线程在获取锁的时候会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,按照FIFO的规则从队列中排队等锁。
-
非公平锁在JAVA中来说:
- 上来线程就可以直接尝试抢占锁,如果成功,直接获取锁,如果不成功,那就按照类似公平锁的方式来获取锁。
- 优点在于它的吞吐量大。
可重入锁(递归锁)
概述
- 同一线程获取到锁后,可以进入此锁拥有的全部同步代码块
- 以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
Lock lock = new ReentrantLock();//一把锁
//get方法里面调set方法
public void get() {
lock.lock(); //当前线程获取到了此锁
set();//这里调用下面的set()方法
lock.unlock();
}
//get方法里面的set方法
public void set() {
lock.lock(); // 在get中当前线程已经获取到了此锁,这里可以再次获取(重入)
lock.unlock();
}
}
作用
- 避免死锁(两个方法互相调用的情况下,如果锁不可重入,则可能会产生死锁)
实现原理
实现原理实现是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。
如果同一个线程再次请求这个锁,计数器将递增;
每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。
读写锁
概述
读写锁分别指的是读锁和写锁
读锁,相当于共享锁
写锁,相当于独占锁
独占锁
- 指该锁一次只能被一个线程持有。
- 对 ReentrantLock类、synchronized 关键字来说都是独占锁。
共享锁
- 指该锁可可以被多个线程持有。
- 对 ReentrantReadWriteLocl类来说,读锁为共享锁,写锁为独占锁。
ReentrantReadWriteLock
// 获取写锁,独占锁形式
public ReentrantReadWriteLock.WriteLock writeLock() {return writerLock; }
// 获取读锁,共享锁形式
public ReentrantReadWriteLock.ReadLock readLock() {return readerLock; }
自旋锁
概述
是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
- 优点是减少线程上下文切换的消耗。
- 缺点是循环会消耗CPU资源。