java锁-Lock
详细讨论java中Lock接口底层加锁原理和应用。
前言
Lock接口是java中重要的锁之一,ReentrantLock、ReentrantReadWriteLock.WriteLock,ReentrantReadWriteLock.ReadLock是几个重要的实现之一,其中ReentrantLock是我们常用的实现类。本文主要讲解ReentrantLock原理和使用,ReentrantLock基于AQS实现,所以在阅读本文前请先了解AQS原理,对AQS不是很了解请移步《java锁-AQS》
Lock接口源码
public interface Lock {
/**
* 加锁,获取锁过程中如果线程中断,则继续获取不会中断,只在获取锁后才中断当前线程
*/
void lock();
/**
* 加锁,获取锁过程中如果线程中断,直接抛InterruptedException异常,其他线程可以通过中断该线程来竞争资源
*/
void lockInterruptibly() throws InterruptedException;
/**
* 当前线程尝试获取锁
* true: 获取锁成功
* false: 获取锁失败
*/
boolean tryLock();
/**
* 当前线程尝试获取锁在一段时间内
* true: 获取锁成功
* false: 获取锁失败
* @param time 最长等待时间
* @param unit 时间单位
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁
*/
void unlock();
/**
* 返回一个条件对象
*/
Condition newCondition();
}
ReentrantLock使用
/**
* 定义一个可重入锁
*/
private final Lock lock = new ReentrantLock();
public boolean printInfo(){
/**
* 开始上锁
*/
lock.lock();
try{
/**
* 让线程休眠1000毫秒
*/
Thread.sleep(1000);
/**
* 打印一条数据
*/
System.out.println("this is ReentrantLock test information");
System.out.println("当前执行线程=>" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
/**
* 释放锁, lock.unlock() 必须放在finally的第一行代码,防止其他代码出现异常而不能及时释放锁
*/
lock.unlock();
}
return true;
}
至于lockInterruptibly()、tryLock()和tryLock(long time, TimeUnit unit)用法和lock()用法一致,读者自己去测试吧。
ReentrantLock实现原理
在ReentrantLook对象中维护一个sync变量,这个就是Sync对象,Sync对象继承了AbstractQueuedSynchronizer,到这里是不是有一点豁然开朗了,ReentrantLook中所有加锁操作都是基于sync实现的,下面看看源码:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** 定义了一个同步变量*/
private final Sync sync;
/**
* 自定义一个同步器Sycn
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 定义一个加锁抽象方法,有具体子类实现
*/
abstract void lock();
/**
* 非公平获取锁,每一次都直接尝试获取锁,不管有没有其他等待队列
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取AQS中共享资源state的值
int c = getState();
// 若当前未有线程持有
if (c == 0) {
// 直接通过CAS修改state值为acquires(一般acquires等于1)
if (compareAndSetState(0, acquires)) {
// 修改成功直接将当前线程设置为独占线程
setExclusiveOwnerThread(current);
// 返回获取线程成功
return true;
}
}
// 如果当前线程是独占线程,说明是重入操作
else if (current == getExclusiveOwnerThread()) {
// 访问次数叠加
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 修改state为叠加的新值,由于是同一个线程操作不存在线程安全问题
setState(nextc);
// 返回获取线程成功
return true;
}
// 返回获取线程失败
return false;
}
// 尝试释放锁,这是AQS的自定义实现
protected final boolean tryRelease(int releases) {
// 将AQS的state数量减releases(releases一般为1)
int c = getState() - releases;
// 如果当前线程不等独占线程,说明是其他线程操作,则抛出IllegalMonitorStateException异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果AQS的state-1 = 0,说明锁释放成功,如果是重入加锁多次,则释放次数必须和重入次数一致,方可解锁
if (c == 0) {
// 将解锁状态设置为成功
free = true;
// 设置独占线程为null
setExclusiveOwnerThread(null);
}
// 修改AQS的值
setState(c);
return free;
}
// 判断当前线程是否是独占线程
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 生成一个新的ConditionObject对象
final ConditionObject newCondition() {
return new ConditionObject();
}
}
/**
* 非公平锁的实现
*/
static final class NonfairSync extends Sync {
/**
* 从写加锁落差
*/
final void lock() {
// CAS修改AQS的state值,修改成功则说明state等于0
if (compareAndSetState(0, 1))
// 获取锁成功,将独占线程设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 获取不到锁,则调用AQS的acquire(1)执行获取锁逻辑
acquire(1);
}
// AQS尝试获取锁的实现
protected final boolean tryAcquire(int acquires) {
// 调用父类Sync.nonfairTryAcquire(acquires)尝试获取锁方法
return nonfairTryAcquire(acquires);
}
}
/**
* 公平锁实现方式
*/
static final class FairSync extends Sync {
// 直接调用AQS的acquire(1)执行获取锁逻辑
final void lock() {
acquire(1);
}
// AQS尝试获取锁的实现(这里是公平获取锁的自定义实现)
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取AQS中共享资源state的值
int c = getState();
// c == 0说明当前未有线程持有
if (c == 0) {
/**
* hasQueuedPredecessors() 在当前节点之前是否有等待的队列(这是公平的体现,只有队列头才能获取锁,其他节点必须等待)
* compareAndSetState(0, acquires) CAS修改AQS的state值为acquires,修改成功则说明竞争锁成功
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 竞争锁成功,将当前线程设置为独占线程
setExclusiveOwnerThread(current);
// 返回获得锁成功
return true;
}
}
// 这里是锁重入实现,和非公平锁一致
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
// 默认采用非公平锁模式
public ReentrantLock() {
sync = new NonfairSync();
}
// 可以通过fair参数来选择使用公平锁还是非公平锁
// fair = true 公平锁
// fair = false 非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
// 加锁实现
public void lock() {
sync.lock();
}
// 释放锁实现
public void unlock() {
sync.release(1);
}
}
到这里源码分析就结束了,得出以下几个结论:
1、ReentrantLock 是通过AQS实现加锁
2、ReentrantLock可使用公平锁和非公平锁两种模式,可通过构造函数传fair实现
3、fair = true 启用公平锁,fair = false启用非公平锁
4、为了提高性能默认ReentrantLock 是非公平锁(推荐)
如果对您有帮助,请支持博主点一个鼓励的赞
原创不易,转载请标明来源