互联网JAVA面试常问问题
Posted 小强的进阶之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了互联网JAVA面试常问问题相关的知识,希望对你有一定的参考价值。
前几篇文章,都介绍了JAVA面试中锁相关的知识。其实关于JAVA锁/多线程,你还需要知道了解关于ReentrantLock的知识,本文将从源码入手,介绍可重入锁的相关知识。
ReentrantLock
先来看看ReentrantLock的源码,部分代码块用省略号代替,后面会详细展开介绍:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */
abstract void lock(); /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */
final boolean nonfairTryAcquire(int acquires) {
.......
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
........
} /** * Sync object for non-fair locks */
static final class NonfairSync extends Sync {
.......
} /** * Sync object for fair locks */
static final class FairSync extends Sync {
...........
}
..........
}
可以看出可重入锁的源码中,其实实现了公平锁和非公平锁。
ReentrantLock中有一个静态内部抽象类Sync,然后有NonfairSync和FairSync两个静态类继承了Sync。
其中Sync继承了AQS(AbstractQueuedSynchronizer),接下来的文章中会介绍详细AQS。
我们在使用可重入锁的时候,需要明显的加锁和释放锁的过程。一般在finally代码中实现锁释放的过程。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try { while(条件判断表达式) {
condition.wait();
}
// 处理逻辑
}
finally
{
lock.unlock();
}
非公平锁的实现
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
可以看出,非公平锁在执行lock的时候,会用CAS来尝试将锁状态改成1,如果修改成功,则直接获取锁,用setExclusiveOwnerThread方法讲当前线程设置为自己。如果没有修改成功,则会执行acquire方法来尝试获取锁。其中,nonfairTryAcquire实现如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
可以看出这个方法,其实也是在用CAS尝试将线程状态置为1。其实也是一个多次尝试获取的过程。
所以,对于非公平锁,当一线程空闲时候,其他所有等待线程拥有相同的优先级,谁先争抢到资源即可以获取到锁。
公平锁的实现
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
} /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
}
主要看当公平锁执行lock方法的时候,会调用 acquire方法, acquire方法首先尝试获取锁并且尝试将当前线程加入到一个队列中,所以公平锁其实是维护了一个队列,谁等待的时间最长,当线程空闲时候,就会最先获取资源:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
如果想了解acquireQueued的话,可以参照一下代码:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node); p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node); } }
综上,可重入锁利用CAS原理实现了公平锁和非公平锁,为什么叫做可重入锁呢?其实在代码方法tryAcquire中可以看到,线程可以重复获取已经持有的锁。
if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
今天小强从源码的角度分析了ReentrantLock,希望对正在学习JAVA的你有所帮助。
以上是关于互联网JAVA面试常问问题的主要内容,如果未能解决你的问题,请参考以下文章