ReentrantLock: 就是支持重进入的锁,他表示该锁 能够支持一个线程对资源的重复加锁.除此之外,该锁还支持获取锁时的公平性和非公平性选择;
(一)重进入:
是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性实现需解决两个问题:
(1)线程再次获取锁-->.锁需要去识别获取锁的线程是否是当前线程,如果是,则再次获取成功.
(2)锁的最终释放:-->线程重复n次获取了锁,随后在第n次释放锁之后,其他线程能够获取到该锁.锁中包含一个计数器,当获取锁时+1,当释放锁时-1,当基数为0时,可以释放该锁,其他线程可以能够获取锁对象;
ReentrantLock:lock();
FairSync:lock();
AbstractQueuedSynchronizer:acquires(int args);
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取当前aqs的状态,如果是0,表示当前还没有线程来占有
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//当前线程获取 aqs 成功,将aqs中的 exclusiveThread(独占线程) 设置为当前的限制
setExclusiveOwnerThread(current);
return true;
}
}
//如果之前的 在aqs中的独占线程和当前的线程相同,那么就将state+1;当释放线程的时候 是-1一个个释放,直到state为0时候,才完全释放
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
当获取锁失败之后:
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//将当前线程设置成一个node,在aqs中以链表的形势存放,然后根据链表一个个获取锁,
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//当前线程 阻塞
selfInterrupt();
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
(二)公平与非公平获取锁的区别:
公平性:公平性与否是针对获取锁而言,如果一个锁时公平的,那么锁获取的顺序就应该符合请求的绝对的时间顺序,也就是FIFO.[1,2,3,4] 获取锁的顺序依次是 1,2,3,4
非公平性: 锁在获取的过程中,一个线程可能多次获取锁,刚释放的线程再次获取同步状态(当一个线程请求一个锁时,只要获取了同步状态,即成功获取了锁)的几率非常大,使得其他线程只能在同步状态下等待. [1,2,3,1,4,6] --> 输出则是:1,1,2,3,4,6
ReadWriteLock: 大部分锁都是排他锁,这些锁在同一时刻只能允许一个线程进行访问,而读写锁在同一时刻可以允许多个线程同时访问,但是在写线程的时候,其他写线程和读线程将会被阻塞.读写锁维护了一个锁,一个读锁和写锁.