继续之前文章。
无论是HashMap,LinkedHashMap,ArrayList等都是非线程安全的,在并发情况下会出现问题,而Jdk也提供了concurrent包,该包下边主要是关于线程安全相关的类,例如ConcurrentHashMap,ConcurrentHashMap的实现原理不在这里阐述了,这里主要讲解跟它有关的锁问题ReentrantLock(重入锁),CAS(比较与交换)
ReentrantLock
- 说明
分为公平锁和非公平锁两个都继承内部类Sync,各自实现,而构造方法也可以指定实例化时采用公平锁还是非公平锁。
- 公平锁:会按照请求的顺序获取锁,如果锁已经被占用,则新来的请求放到队列中。
- 非公平锁:不是按照请求顺序获取锁,存在插队现象。
- 类的结构
//内部类
private final Sync sync;
//定义了内部类
abstract static class Sync extends AbstractQueuedSynchronizer
//非公平锁继承内部类
static final class NonfairSync extends Sync
//公平锁继承内部类
static final class FairSync extends Sync
- ReentrantLock构造方法有两个:无参和有参(指定采用公平和非公平)
分别看下是如何初始化的。
public ReentrantLock() {
//默认非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//若fair为true,则是公平锁,否则非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
- 接下来看下公平锁lock()是如何实现加锁的
public void lock() {
//根据实例化的锁走对应的子类实现
sync.lock();
}
//公平锁
final void lock() {
//调用父类AbstractQueuedSynchronizer中的方法
acquire(1);
}
public final void acquire(int arg) {
//tryAcquire子类重新了所以调用子类,尝试获取锁,若能获取到直接返回
if (!tryAcquire(arg) &&
//获取不到则直接加入到等待队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态,获取状态0
int c = getState();
if (c == 0) {
//判断队列中是否有比自己优先级更高的线程,若没有则通过CAS获取锁
if (!hasQueuedPredecessors() &&
//通过CAS获取锁,具体如何获取后面说
compareAndSetState(0, acquires)) {
//设置自己持有锁,并返回true
setExclusiveOwnerThread(current);
return true;
}
}
//若当前持有锁的线程是自己,则将重入次数累加
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//设置新的状态,并返回true
setState(nextc);
return true;
}
return false;
}
//上边代码中的hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
//Node包含下个线程和上个线程,采用这种方式形成一个链表
//Node中有等待状态waitStatus,前节点prev,下一个节点next及当前线程thread,
//下一个等待线程nextWaiter
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
//判断是否有等待获取锁的节点
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
公平锁的过程是:
首先尝试获取锁,获取锁的过程中会判断队列中是否有比自己优先级高的线程若有则返回false,加锁失败,若没有则通过CAS获取锁,并设置自己为当前持有锁的线程,返回true;
若是重入则次数加1,并返回true;若获取不到所则将自己加到队列中等待获取锁。
5. 非公平锁,会有插队的现象,导致这中现象是因为每次lock都会先获取锁,不会检查队列中是否包含等待线程,若获取到则插队成功,否则失败添加到队列中。
final void lock() {
//采用CAS方式,若获取到则设置自己为所的持有者否则按照公平锁的方式请求。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- tryLock() 尝试获取锁,获取到就返回true,否则失败false
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//与公平锁有点类似
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//与公平锁不同之处,不判断队列,直接通过CAS方式获取锁
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");
setState(nextc);
return true;
}
return false;
}
- tryLock(long timeout, TimeUnit unit)指定等待时间,到时还未获取到锁则返回false。
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
//若线程中断则返回异常
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquire尝试获取锁,非公平和公平各自实现,可以看上边代码
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
CAS
在ReentrantLock中核心部分采用的还是CAS(比较与交换,Compare and swap)获取锁,CAS是CPU的指令,意思是“认为val的值是1,如果是1则将val的值更新为2,否则不做更改,并返回V实际的值”。
先介绍到这里有什么不妥之处也请看到的大咖纠正。
也可以关注公众号进行探讨
![公众号](https://i-blog.csdnimg.cn/blog_migrate/d266b4a05771d4708f5ffab3505ba7a5.png)