3.自定义的lock(可重入)
public class MyLock implements Lock {
private boolean isLock=false;
//用于实现可重入锁
private Thread lockBy=null;
private int lockCount=0;//线程计数
@Override
public synchronized void lock() {
Thread currentThread = Thread.currentThread();
try {
while(isLock && currentThread!=lockBy) {//判断当前线程是否等于上次线程,如果不是就等待
wait();//等待
}
isLock=true;
lockBy=currentThread;
lockCount++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public synchronized void unlock() {
Thread currentThread = Thread.currentThread();
if (lockBy==currentThread) {//判断当前线程是否等于之前的线程
lockCount--;//线程计数--
if (lockCount==0) {//如果过线程数等于0,唤醒其他线程
isLock=false;
notify();//唤醒
}
}
}
//暂时只实现lock及unlock方法
}
/**
* 类描述: 验证线程安全性
*/
public class Sequence2 {
private int i;
private MyLock myLock=new MyLock();
public int getNext() {
myLock.lock();
try {
i++;
} catch (Exception e) {
e.printStackTrace();
}finally {
myLock.unlock();
}
return i;
}
public static void main(String[] args) {
Sequence2 s = new Sequence2();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 类描述: 验证自定义锁线程是否可重入
*/
public class Demo {
private MyLock myLock=new MyLock();
private void a() {
try {
myLock.lock();
System.out.println("aaaaa");
b();
} catch (Exception e) {
e.printStackTrace();
}finally {
myLock.unlock();
}
}
private void b() {
try {
myLock.lock();
System.out.println("bbbbb");
} catch (Exception e) {
e.printStackTrace();
}finally {
myLock.unlock();
}
}
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.a();
}
}).start();
}
}
十:AbstractQueuedSynchronizer(AQS)源码解析
对于AbstractQueuedSynchronizer的源码解析,我们以ReentrantLock为例,因为ReentrantLock实现锁的机制,底层是使用AbstractQueuedSynchronizer的。
1.AbstractQueuedSynchronizer的数据结构
AbstractQueuedSynchronizer是一个基于双向链表来实现FIFO(先进先出的队列),所以,在其内部创建了一个Node内部类,表示链表的各个节点的。
其中Sync queue,即同步队列,是双向链表,包括head结点和tail结点,head结点主要用作后续的调度。而Condition queue不是必须的,其是一个单向链表,只有当使用Condition时,才会存在此单向链表。并且可能会有多个Condition queue。
static final class Node {
// 模式,分为共享与独占
// 共享模式
static final Node SHARED = new Node();
// 非共享模式
static final Node EXCLUSIVE = null;
// 结点状态
// CANCELLED,值为1,表示当前的线程被取消
// SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
// CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
// PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行
// 值为0,表示当前节点在sync队列中,等待着获取锁
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
// 结点状态
volatile int waitStatus;
// 前驱结点
volatile Node prev;
// 后继结点
volatile Node next;
// 结点所对应的线程
volatile Thread thread;
// 下一个等待者
Node nextWaiter;
// 结点是否在共享模式下等待
final boolean isShared() {
return nextWaiter == SHARED;
}
// 获取前驱结点,若前驱结点为空,抛出异常
final Node predecessor() throws NullPointerException {
// 保存前驱结点
Node p = prev;
if (p == null) // 前驱结点为空,抛出异常
throw new NullPointerException();
else // 前驱结点不为空,返回
return p;
}
// 无参构造函数
Node() {}
// 构造函数
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
// 构造函数
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
每个被阻塞的线程都会被封装成一个Node结点,放入队列。每个节点包含了一个Thread类型的引用,并且每个节点都存在一个状态
2.ReentrantLock是如何加锁的
因为ReentrantLock是Lock接口的实现类,所以在使用ReentrantLock时是通过lock()方法实现加锁
发现调用了sync.lock();这个方法,而sync又是那个类呢?
发现sync是Sync的实例,而Sync又继承了AbstractQueuedSynchronizer,所以ReentrantLock底层是通过AbstractQueuedSynchronizer实现所得机制的。通过查看Sync的实现发现
Sync有两个实现类,一个是公平同步器,一个是非公平的同步器,这两个同步器都实现了Sync的lock()及tryAcquire()抽象方法。而在这两个同步器的lock()方法中都调用了acquire(1)方法,并且都传入1作为参数,而这个acquire()方法其实就是AbstractQueuedSynchronizer中的方法。
ReentrantLock是一个可重入的,也是一个有公平和非公平的锁,所以在调用ReentrantLock的lock()方法时,实际就是调用公平或非公平同步器的lock()方法,而这两个同步器调用有调用AbstractQueuedSynchronizer的acquire()方法,在AbstractQueuedSynchronizer的acquire()方法中首先调用tryAcquire()方法,而两个同步器有实现了Sync的tryAcquire()抽象方法,所以就又调用了两个同步器的tryAcquire()方法。
对于非同步同步器的tryAcquire()方法,其内部调用nonfairTryAcquire()方法,传入的参数就是在lock()方法中调用acquire()传入的1
进入到nonfairTryAcquire()方法中
//acquires传入为1
final boolean nonfairTryAcquire(int acquires) {
//获得当前线程
final Thread current = Thread.currentThread();
//获得状态,初始值为0
int c = getState();
if (c == 0) {
//将传入值设置为状态
if (compareAndSetState(0, acquires)) {
//将当前线程设置为获得锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果状态不为0,判断获得锁的线程是不是当前线程(判断是否是重入),如果是
else if (current == getExclusiveOwnerThread()) {
//将状态加上传入值
int nextc = c + acquires;
if (nextc < 0) //如果过状态<0,抛出异常
throw new Error("Maximum lock count exceeded");
setState(nextc);//否则设置状态
return true;
}
//以上条件都不满足,返回false
return false;
}
tryAcquire()执行完毕
对于tryAcquire()返回false,说明其他线程未获得锁,就执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,首先执行addWaiter()方法,这个方法就是将当前线程包装为一个node加入到双向链表中,传入参数为null。
// 添加等待者
private Node addWaiter(Node mode) {
// 新生成一个结点,默认为独占模式
Node node = new Node(Thread.currentThread(), mode);
// 保存尾结点
Node pred = tail;
if (pred != null) { // 尾结点不为空,即已经被初始化
// 将node结点的prev域连接到尾结点
node.prev = pred;
if (compareAndSetTail(pred, node)) { // 比较pred是否为尾结点,是则将node设置为尾结点
// 设置尾结点的next域为node
pred.next = node;
return node; // 返回新生成的结点
}
}
enq(node); // 尾结点为空(即还没有被初始化过),或者是compareAndSetTail操作失败,则入队列
return node;
}
如果进入enq(node);方法
// 入队列
private Node enq(final Node node) {
for (;;) { // 无限循环,确保结点能够成功入队列
// 保存尾结点
Node t = tail;
if (t == null) { // 尾结点为空,即还没被初始化
if (compareAndSetHead(new Node())) // 头结点为空,并设置头结点为新生成的结点
tail = head; // 头结点与尾结点都指向同一个新生结点
} else { // 尾结点不为空,即已经被初始化过
// 将node结点的prev域连接到尾结点
node.prev = t;
if (compareAndSetTail(t, node)) { // 比较结点t是否为尾结点,若是则将尾结点设置为node
// 设置尾结点的next域为node
t.next = node;
return t; // 返回尾结点
}
}
}
}
addWaiter()方法执行完毕后,双向链表就维护好了。
接着执行acquireQueued()方法,arg=1,并将新创建的node传入进去
// sync队列中的结点在独占且忽略中断的模式下获取(资源)
final boolean acquireQueued(final Node node, int arg) {
// 标志
boolean failed = true;
try {
// 中断标志
boolean interrupted = false;
for (;;) { // 无限循环
// 获取node节点的上一个结点
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 上一个节点为头结点并且成功获得锁
setHead(node); // 设置头结点
p.next = null; // 设置下一个节点为null
failed = false; // 设置标志
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先获取当前节点的上一个节点,如果上一个节点是头结点并且能够获取(资源),代表该当前节点能够占有锁,设置头结点为当前节点,返回。否则,调用shouldParkAfterFailedAcquire()和parkAndCheckInterrupt()方法,首先,我们看shouldParkAfterFailedAcquire()方法。
// 当获取(资源)失败后,检查并且更新结点状态
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前驱结点的状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 状态为SIGNAL,为-1
// 可以进行park操作
return true;
if (ws > 0) { // 表示状态为CANCELLED,为1
do {
node.prev = pred = pred.prev;//移除状态为CANCELLED的节点
} while (pred.waitStatus > 0); // 找到pred结点前面最近的一个状态不为CANCELLED的结点
// 赋值pred结点的next域
pred.next = node;
} else { // 为PROPAGATE -3 或者是0 表示无状态,(为CONDITION -2时,表示此节点在condition queue中)
// 比较并设置前驱结点的状态为SIGNAL,当设置为SIGNAL,第一个if判断就成立了,返回true
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
// 不能进行park操作
return false;
}
parkAndCheckInterrupt()方法
private final boolean parkAndCheckInterrupt() {
// 使当前线程中断
LockSupport.park(this);
return Thread.interrupted(); // 当前线程是否已被中断,并清除中断标记位
}
parkAndCheckInterrupt函数里的逻辑是首先执行park操作,即禁用当前线程,然后返回该线程是否已经被中断。到这里,新加的node节点就处于等待状态。
3.ReentrantLock是如何解锁的
调用同步器的release()方法,传入1为参数
调用tryRelease()方法,传入1
//AQS的tryRelease方法默认实现是抛出异常,需要具体的子类实现,这里就是公平/非公平同步器实现的
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//获得当前状态-1
if (Thread.currentThread() != getExclusiveOwnerThread())//当前线程不等于获得所得线程,抛出异常
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//相减后的状态等于0,说明所有的锁已经释放完毕
free = true;
setExclusiveOwnerThread(null);//设置获得所得线程为null
}
setState(c);//设置状态
return free;
}
当tryRelease()返回false时,表示锁释放失败,反之返回true,或的头结点,调用unparkSuccessor(h);方法,传入头结点
//该函数的作用就是为了释放头节点的后继结点
private void unparkSuccessor(Node node) {
//获得头结点转台
int ws = node.waitStatus;
if (ws < 0)//如果头结点状态<0
compareAndSetWaitStatus(node, ws, 0);//比较并设置头结点状态为0
Node s = node.next;//获得头结点的下一个节点
if (s == null || s.waitStatus > 0) {//如果头结点下一个节点为null,说明双向链表为null
s = null;
//从尾结点开始从后往前开始遍历
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0) 找到等待状态小于等于0的结点,找到最前的状态小于等于0的结点
s = t;//保存节点
}
if (s != null)//如果头结点下一个节点不为null,唤醒头结点的后续节点
LockSupport.unpark(s.thread);
}
到这里,AbstractQueuedSynchronizer就解析完毕,对于AbstractQueuedSynchronizer的分析,最核心的就是队列的分析, 每一个结点都是由前一个结点唤醒,当结点发现前驱结点是head并且尝试获取成功,则会轮到该线程运行,当结点的状态为SIGNAL时,表示后面的结点需要运行。
十一:基于AbstractQueuedSynchronizer重写自己的锁
public class MyLock implements Lock{
private Sync sync=new Sync();
private class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
int state = getState();
Thread currentThread = Thread.currentThread();
if (state==0) {//初始值
//第一个线程进来,可以拿到锁,可以返回true
if (compareAndSetState(0, arg)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
if (getExclusiveOwnerThread()==currentThread) {
//第二个线程进来,如果当前进来的线程和获得所得线程是同一个,则能拿到锁,并更新状态值
setState(++state);
return true;
}
//第二个线程进来,如果当前进来的线程和获得所得线程不是同一个,拿不到锁,返回false
return false;
}
@Override
protected boolean tryRelease(int arg) {
//所得获取和释放一一对应,调用此方法线程一定是当前线程
if(getExclusiveOwnerThread()!=Thread.currentThread()) {
throw new RuntimeException();
}
int c=getState()-arg;
boolean tag=false;
if(c==0) {
setExclusiveOwnerThread(null);
tag=true;
}
setState(c);
return tag;
}
Condition newCondition() {
return new ConditionObject();
}
}
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
public class Sequence {
private int i;
private Lock lock = new MyLock();
public int getNext() {
lock.lock();
try {
i++;
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
return i;
}
public void a() {
lock.lock();
System.out.println("aaaaaaaaaaa");
b();
lock.unlock();
}
public void b() {
lock.lock();
System.out.println("bbbbbbbbbbbb");
lock.unlock();
}
public static void main(String[] args) {
Sequence s = new Sequence();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
s.a();//测试可重入
// System.out.println(Thread.currentThread().getName() + " " + s.getNext()());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
/* new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();*/
}
}
十二:公平锁及ReentrantLock实现公平与非公平锁原理
1.公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
2.公平锁原理:通过一个先进先出队列,实现公平锁,每一个线程进来就向队列中加一个节点,每个节点中有自己的wait()方法及notify()方法,这样每次唤醒就只唤醒当前节点。
3.原理模仿:
//描述队列中的等待线程
public class QueueObject {
//当前节点是否被唤醒
private boolean isNotified = false;
public synchronized void doWait() throws InterruptedException {
while (!isNotified) {//只要没有被唤醒就一直等待
this.wait();
}
this.isNotified = false;
}
public synchronized void doNotify() {//唤醒当前节点
this.isNotified = true;
this.notify();
}
public boolean equals(Object o) {
return this == o;
}
}
//模仿公平锁
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
//通过ArrayList模仿线程队列
private List<QueueObject> waitingThreads = new ArrayList<QueueObject>();
//加锁
public void lock() throws InterruptedException {
//创建节点
QueueObject queueObject = new QueueObject();
synchronized (this) {
//加入到等待队列
waitingThreads.add(queueObject);
}
try {
//调用节点的doWait()方法
queueObject.doWait();
} catch (InterruptedException e) {
synchronized (this) {
waitingThreads.remove(queueObject);
}
throw e;
}
}
//释放锁
public synchronized void unlock() {
if (this.lockingThread != Thread.currentThread()) {
throw new IllegalMonitorStateException("Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if (waitingThreads.size() > 0) {
//获取第一个节点,唤醒
waitingThreads.get(0).doNotify();
}
}
}
4.ReentrantLock如何实现公平锁
通过传入一个boolean值来创建是否是公平锁
公平锁相对于非公平锁在tryAcquire()方法中就多了一步hasQueuedPredecessors()方法的调用,用来判断在当前线程是否有前置节点,如果过有就返回true,tryAcquire()就返回false,只有前置节点获取到锁后,当前线程才能获取锁,所以返回false。