基础巩固篇 —— 对锁的理解
一、非公平锁
非公平锁是抢占式的,有优先级区分的线程争夺锁。
包括:
synchronized关键字
new ReentrantLock()默认创建的也是非公平锁
二、公平锁
公平锁是先到先得的原则,排队获取。
new ReentrantLock(true)
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
三、可重入锁(递归锁)
可重入锁也叫递归锁,指的是同步方法内调用加锁方法,只需要获取最外层锁即可畅通无阻,内部的其他锁不再需要获取,可直接访问。这种设计可以极大的减少锁控制(如果是可重入锁,只需要控制最外层的锁即可),避免锁控制混乱或释放不及时导致死锁发生的概率。
public synchronized void me01() {
me02();
}
public synchronized void me02() { }
四、自旋锁
自旋锁是通过CAS原则,不停的比对以达到阻塞效果,比对成功后继续执行程序已达到释放锁的效果。这样不停循环比对的过程需要消耗系统资源。
private volatile AtomicReference<Thread> thread = new AtomicReference<>();
// 自旋加锁,如果当前原子包装线程对象是null就更新为当前对象
public void locak() {
while (!thread.compareAndSet(null, Thread.currentThread())) {}
}
public void getResource() {
// 获得锁之后,其他线程再次调用该方法,通过比对不一致则一直空循环比对
locak();
// 执行逻辑
// 释放锁,恢复为null,其他线程可以取锁
unLock();
}
// 释放锁,
public void unLock() {
while (!thread.compareAndSet(Thread.currentThread(), null)) {}
五、读写锁
不管是synchronized还是ReentrantLock都是只允许一个线程对资源进行读和写操作,这样的效率是极低的。最理想的状态是可以由多个线程读的过程中限制只有一个线程的写操作。核心是读写分离,举个例子:
高铁站候车室的多个显示屏C端显示着实时车次信息,每个旅客相当于一个个线程读,而候车信息的更新只允许有权限的某一个线程进行写操作。
Lock writeLock = new ReentrantReadWriteLock().writeLock();
public void getResource() {
// 获取锁
writeLock.lock();
try{
// 原子业务逻辑
} finally {
// 释放锁
writeLock.unlock();
}
}