Lock
JDK1.5之后增加Java.concurrent.locks提供了与内建锁完全不同的实现多线程共享机制,失去了内建锁隐式的加锁与解锁过程,增加了可中断的获取锁以及超时获取锁以及共享锁等内建锁不具备的特性.
lock接口的API有以下几种:
I . void lock(); / /获取锁
II . void lockInterruptibly()throws InterruptedException; / / 获取锁的过程能够响应中断(lock独有)
III . boolean trylock(); / / 获取锁返回true,反之返回false,可以响应中断
IV . boolean trylock(long time,TimeUnit unit); / / 在3的基础上增加了超时等待机制,规定时间内为获取到锁线程直接返回false.(lock独有)
V . void unlock(); / / 解锁
AbstractQueueSynchronizer(AQS)
AbstractQueueSynchronizer(AQS同步器) 是lock体系中最核心的存在.
要使用AQS,推荐使用静态内部类继承AQS,覆写AQS的protected用来改变同步状态的方法,其他方法主要是实现排队与阻塞机制,状态更新使用getState(),setState(),compareAndSetState().用这种方法我们可以自己实现一个Lock.
自己实现一个NBLock:
package com.matajie;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 自己定义一个锁
*
* Data: 2019 - 12 - 17
* Time: 7:29
* author:我的程序才不会有Bug!
*/
class NBLock implements Lock{
private Sync sync = new Sync();
//定义一个静态内部类继承AQS
static class Sync extends AbstractQueuedSynchronizer{
//覆写它的protected方法.
@Override
//规定同步状态为1,为1说明拿到锁,不为1说明没拿到锁
protected boolean tryAcquire(int i) {
if(i != 1){
throw new RuntimeException("i不为1");
}
//为1,尝试让它获取锁
if(compareAndSetState(0,1)){
//当前线程成功获取到锁,将获取锁的线程置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
//获取到了return true
return true;
}
//否则return false
return false;
}
@Override
protected boolean tryRelease(int i) {
//getState获取当前状态,如果为0(无锁),抛出异常
if(getState() == 0){
throw new IllegalMonitorStateException();
}
//否则将当前线程置空
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
//如果=,表示拿到锁了,否则没拿到
return getState() == 1;
}
}
//lock接口方法
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long l, TimeUnit timeUnit) throws InterruptedException {
return sync.tryAcquireNanos(1,1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return null;
}
}
class MyThread implements Runnable{
Lock lock = new NBLock();
@Override
public void run() {
try {
lock.lock();
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class Test{
public static void main(String[] args) {
MyThread myThread = new MyThread();
for(int i = 0;i<10;i++){
Thread thread = new Thread(myThread);
thread.start();
}
}
}
进入Debug模式结果如下:
这一点可以看出:lock面向使用者,定义了使用者与锁的交互的接口,隐藏了实现细节,AQS面向锁的实现者,简化了锁的实现方式,避免了同步状态管理,线程排队,线程等待与线程唤醒等操作.
AQS源码:独占锁的获取(acquire)与释放(release)
独占锁的获取
acquire()
tryAcquire再次使用CAS尝试获取同步状态,若成功方法直接返回,当前线程置为持有线程.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
addWaiter()
若失败,则执行addWaiter(),将当前线程封装为Node节点后尾插入同步队列中.
private Node addWaiter(Node mode) {
//将当前线程以指定模式封装为Node节点
Node node = new Node(Thread.currentThread(), mode);
//拿到当前同步队列的尾结点
Node pred = tail;
//
if (pred != null) {//如果tail不为空
node.prev = pred;//将新建节点尾插到队尾
//将当前节点使用CAS尾插入同步队列
if (compareAndSetTail(pred,node)) {
pred.next = node;
return node;
}
}
//当前队列为空或CAS尾插失败时
this.enq(node);
return node;
}
enq()
当前同步队列为空或者尾插失败,执行enq(),当同步队列为空时完成队列初始化操作以及不断CAS将当前节点尾插入同步队列
在这里插入代码片private Node enq(final Node node) {
//死循环-不断自旋
for(;;){
//拿到尾结点
Node t = tail;
//当前队列为空
if (t == null) {
//完成队列的初始化操作,lazy-load(第一次用才初始化)
if (compareAndSetHead(new Node())) {
tail = head;
}
} else {//队列不为空
node.prev = t;
//不断将当前节点使用CAS尾插入同步队列中,直到成功为止
if (this.compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
acquireQueued()
若addWaiter()成功,则执行acquireQueued()
final boolean acquireQueued(final Node node, int arg) {
//设置失败状态,初始化为true
boolean failed = true;
try {
//设置中断状态,默认为false
boolean interrupted = false;
//不断自旋
for(;;) {
//拿到当前节点前驱节点
final Node p = node.predecessor();
//当前节点前驱节点为头结点并且再次获取同步状态成功
if (p == head && tryAcquire(arg)) {
//将当前节点置为头结点
setHead(node);
//将前驱节点出队
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
interrupted = true;
}
}
} finally {
if (failed) {
//将当前节点置为取消状态,node.waitStutas = 1
this.cancelAcquire(node);
}
}
}
节点从同步队列获取同步状态的前提条件是if(p == head && tryAcquire(arg)
只有当前驱节点为头结点时,线程才有机会获取同步状态.
setHead()
前驱节点为头结点并且当前线程获取同步状态成功,执行setHead(),将当前节点置为头结点并且清空节点中线程,将前驱结点出队
private void setHead(Node node) {
this.head = node;
node.thread = null;
node.prev = null;
}
shouldParkAfterFailedAcquire()
当前线程获取同步状态失败时,首先调用shouldParkAfterFailedAcquire(),尝试将前驱节点状态改为Node.SIGNAL,表示此时当前节点应该被阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取前驱节点状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) {
//表示应该将当前节点阻塞
return true;
} else {
//前驱节点被取消
if (ws > 0) {
//一直向前找到节点状态不是取消状态的节点
do {
node.prev = pred = pred.prev;
} while(pred.waitStatus > 0);
pred.next = node;
} else {
//将前驱节点状态置为signal,-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
}
parkAndCheckInterrupt()
一直自旋直到当把前驱节点置为-1时执行parkAndCheckInterrupted(),阻塞当前节点线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
独占锁的释放:release()
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
//获取到当前同步队列的头结点
if (h != null && h.waitStatus != 0) {
unparkSuccessor(h);
}
return true;
} else {
return false;
}
}
unparkSuccessor()唤醒距离头结点最近的一个非空节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0) {
compareAndSetWaitStatus(node, ws, 0);
}
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//当头结点的下一个节点为空时,
//从同步队列尾部开始一直向前找到距离头节点最近的一个非空节点
for(Node t = tail; t != null && t != node; t = t.prev) {
if (t.waitStatus <= 0) {
s = t;
}
}
}
if (s != null) {
LockSupport.unpark(s.thread);
}
}
独占锁的特性
1.可中断获取锁
acquireInterrupted()
void lockInterruptibly() throws InterruptedException;
最终会调用AQS acquireInterruptibly(int arg)模板方法.
public final void acquireInterruptibly(long arg) throws InterruptedException {
//增加了对中断状态的判断.如果检测线程中断状态改变,抛出中断异常后方法直接退出
if (Thread.interrupted()) {
throw new InterruptedException();
} else {
if (!tryAcquire(arg)) {
doAcquireInterruptibly(arg);
}
}
}
doAcquireInterrupted()
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for(;;) {
final Node p = node.predecessor();
if (p == head && this.tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return;
}
if(shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt());
//线程中断时若检测到中断抛出中断异常后退出
throw new InterruptedException();
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
2.超时等待获取锁
boolean tryLock(long time,TimeUnit unit)throws InterruptedException;
该方法本质调用AQS的模板方法tryAcquireNanos(int arg,long nanosTimeout).
tryAcquireNanos()
public final boolean tryAcquireNanos(long arg, long nanosTimeout) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos()
private boolean doAcquireNanos(long arg, long nanosTimeout) throws InterruptedException {
//传入时间小于0,方法直接退出,线程获取锁失败
if (nanosTimeout <= 0L)
return false;
//根据超时时间计算出截止时间.
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for(;;){
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return true;
}
//再次计算截止时间-当前时间值
nanosTimeout = deadline - System.nanoTime();
//已超时,线程直接退出
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > 1000L)
//在超时时间内仍未被唤醒,线程退出.
LockSupport.parkNanos(this, nanosTimeout);
if(Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}