ReentrantLock的玩法

首先看下ReentrantLock的构造方法

    /**
     * 无参构造方法默认为非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 有参构造方法,true为公平锁,false为非公平锁
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平锁和非公平锁有什么区别

/**
* 公平锁加锁
*/
final void lock() {
		acquire(1);
}
 
/**
* 非公平锁加锁
*/
final void lock() {
    //线程入队前争夺资源
	if (compareAndSetState(0, 1))
		setExclusiveOwnerThread(Thread.currentThread());
	else
		acquire(1);
}

这地方解释下,ReentrantLock是内部维护了一个AbstractQueuedSynchronizer抽象队列同步器简称AQS,

内部维护了线程入队出队的规则

private transient volatile Node head; //队列头节点
private transient volatile Node tail; //队列尾节点
private volatile int state; //0表示没有线程持有锁,1表示当前有线程持有锁,>1表示重入锁
//维护的节点结构
static final class Node {
 volatile Node prev;//当前节点前驱
 volatile Node next;//当前节点后继
 volatile Thread thread;//节点存储的线程信息
}

从代码和多线程竞争图示可看出:公平锁(线程直接入队),非公平锁(线程入队之前会尝试持有锁,失败排队),入队后就是一日排队终身排队,不能插队竞争

package com.hx.zbhuang.mianEntrance;
import java.util.concurrent.locks.ReentrantLock;
public class LockExplore {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                lock.lock();
                System.out.println("aaa");
                lock.unlock();
            }
        };
        t1.start();

        Thread t2 = new Thread("t1"){
            @Override
            public void run() {
                lock.lock();
                System.out.println("aaa");
                lock.unlock();
            }
        };
        t2.start();

        Thread t3 = new Thread("t1"){
            @Override
            public void run() {
                lock.lock();
                System.out.println("aaa");
                lock.unlock();
            }
        };
        t3.start();
    }
}

查看加锁过程:

1.首先调用tryAcquire方法,单线程,多线程交替执行,重入锁时不需要入队。

/**
* 加锁
* @arg 每次加锁锁标识state+arg(数值为1)
*/
public final void acquire(int arg) {
//首先查看是否已经存在线程竞争,如果是单线程或者交替执行,直接设置线程持有锁,无需创建队列
//如果是线程再次加锁(重入锁),直接将锁状态标识+args(1),重置锁状态标识
if (!tryAcquire(arg) &&
    //如果存在线程竞争,先初始化队列头结点,将当前线程封装成node节点入队
	acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    //防止线程竞争锁资源时被interrupt
	selfInterrupt();
}

 

 /**
 * 尝试获取锁,如果返回false则需要入队,返回true当前线程持有锁
 * @acquires每次加锁锁标识state+acquires(数值为1)
 */
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()) {
        //将锁状态标识+acquires(默认为1)
		int nextc = c + acquires;
        //可能是线程t1未加锁直接解锁还没抛出异常时线程二执行加锁出现异常
		if (nextc < 0)
			throw new Error("Maximum lock count exceeded");
        //更新重入后锁状态标识
		setState(nextc);
		return true;
	}
	return false;
	}
}

 

/**
* 判断是否需要入队
* 从上面方法可以看出,当前方法返回false,直接去持有锁,返回true的时候需要判断是否存在线程竞争或者
* 是否为重入锁,1.如果为单线程或者多线程交替执行或者为重入锁的时候,不存在锁竞争,因为没有还没有
* 初始化AQS队列,这时候tail=head=null,这时候h!=t为false,方法直接返回false,直接持有锁,2.如果是
* 存在竞争,多个线程排队依次执行,直到最后一个线程获取锁的时候,既只剩下头结点(null节点),这时候
* 新线程来的时候head=new Node(null),tail=null,h!=t,(s = h.next) == null为方法返回true,进行排
* 队,如果持有锁线程执行完,刚排队的线程再次进入,head=tail=null时候,h!=t方法返回false直接持有
* 锁,3.如果存在竞争,多个线程在排队,这时候来个新线程,这时候head=new Node(null);tail=new 
* Node(xxx),h!=t,(s = h.next) != null,不为持有锁的线程s.thread != Thread.currentThread()为 
* true,方法返回true,进行排队,4.如果存在竞争,线程在排队,如第三点,但是这是来的线程是持有锁的
* 线程,s.thread != Thread.currentThread()为false,当前线程重入锁,在tryAcquire将锁状态标识
* state+acquires(默认入参为1)
*/
public final boolean hasQueuedPredecessors() {
	Node t = tail;
	Node h = head;
	Node s;
	return h != t &&
		((s = h.next) == null || s.thread != Thread.currentThread());
}

2.存在竞争情况下需要先初始化队列调用addWaiter(Node.EXCLUSIVE)

/**
* 根据当前队列是否初始化,如果队列初始化需要先初始化一个内容为null的头结点标识当前持有锁线程,然
* 后在header节点之后添加排队的节点,主要存储排队的线程信息
* @node 当前线程后继节点
*/
private Node addWaiter(Node mode) {
    //将当前线程封装成node节点
	Node node = new Node(Thread.currentThread(), mode);
	//获取队列的尾节点,注意如果队列初始化过tail节点一定存在,至少为new Node()空节点不为null
	Node pred = tail;
    //如果尾节点不为空(队列初始化过)
	if (pred != null) {
        //将当前线程的前驱指向尾节点
		node.prev = pred;
        //将当前节点设置为队列的尾节点
		if (compareAndSetTail(pred, node)) {
            //将上一尾节点后继指向当前线程,当前线程入队
			pred.next = node;
			return node;
		}
	}
    //队列未初始化则初始化队列
	enq(node);
	return node;
}
/**
* 初始化AQS队列
* 首先判断队列是否为空,队列未初始化的时候第一次循环添加new Node()内容为null的头结点
* 第二次循环将当前线程添加为尾节点
* @node当前线程封装的节点
*/
private Node enq(final Node node) {
	for (;;) {
		Node t = tail;
        //判断尾节点是否为空
		if (t == null) {
            //如果尾节点为null,队列未初始化,先添加一个空的header节点标识持有锁线程
			if (compareAndSetHead(new Node()))
				tail = head;
		} else {
			node.prev = t;
            // header节点添加后将当前线程添加到队列尾部
			if (compareAndSetTail(t, node)) {
				t.next = node;
				return t;
			}
		}
	}
}

3.入队

/**
 * @node当前线程封装的节点
 * @arg 每次加锁锁标识state+arg(数值为1)
 */
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)) {
                //加锁成功将持有锁线程设置为head节点断开和上一head节点的联系
				setHead(node);
                //断开上一head节点所有连接
				p.next = null; // help GC
                //加锁状态置为成功
				failed = false;
                //返回加锁过程是否被interrupted标识
				return interrupted;
			}
            //判断当前线程前一节点的等待状态,默认初始状态为0,第一次循环进入                            
            //shouldParkAfterFailedAcquire方法会将状态置为-1,第二次进入返回true
            //判断是否被interrupted ,如果被中断修改中断状态标识
			if (shouldParkAfterFailedAcquire(p, node) &&
                //将上一线程的waitstatu置为-1后当前线程park,并且检查加锁过程中当前是否被中断
				parkAndCheckInterrupt())
                //如果当前线程被中断,则设置中断标识为true
				interrupted = true;
		}
	} finally {
        //如果加锁过程出现异常,将节点从队列中取消
		if (failed)
			cancelAcquire(node);
	}
}

 

/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED =  1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL    = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
 * waitStatus value to indicate the next acquireShared should
 * unconditionally propagate
 */
static final int PROPAGATE = -3;
/**
* 获取当前节点的上一节点
*/
final Node predecessor() throws NullPointerException {
	Node p = prev;
	if (p == null)
		throw new NullPointerException();
	else
		return p;
}
/**
* 线程第一次进来查看前驱线程封装节点的ws初始为0会将前一节点的ws置为-1,退出方法,第二次
* 进入方法发现前驱节点状态为-1则返回true,表示当前线程可以parking了
* 每次线程parking前先将前驱的ws置为-1表示可unparking状态
* @pred当前线程封装节点的前一节点
* @node当前线程封装节点
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
	
	int ws = pred.waitStatus;
	//如果当前线程节点的前一节点ws为-1则当前线程节点需要park
	if (ws == Node.SIGNAL)
		/*
		 * This node has already set status asking a release
		 * to signal it, so it can safely park.
		 */
		return true;
	if (ws > 0) {
		/*
		 * Predecessor was cancelled. Skip over predecessors and
		 * indicate retry.
		 */
		do {
			node.prev = pred = pred.prev;
		} while (pred.waitStatus > 0);
		pred.next = node;
	} else {
		//将当前节点的上一节点ws设置为-1
		compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
	}
	return false;
}
/**
 *将当前线程park,并且返回当前线程是否被中断状态
 */
private final boolean parkAndCheckInterrupt() {
	LockSupport.park(this);
	return Thread.interrupted();
}

解锁

/**
* 释放锁资源
*/
public void unlock() {
  sync.release(1);
}
/**
 * 解锁
 * @arg 解锁状态标识state-arg(数值为1)
 */
public final boolean release(int arg) {
	//解锁更新锁状态标识,重入锁不将队列下一节点解阻塞,只有完全释放锁才能将下一节点解阻塞
	if (tryRelease(arg)) {
		Node h = head;
		//如果不为持有锁节点,既为排队节点,ws!=0(被后一节点置为-1)
		if (h != null && h.waitStatus != 0)
			//解锁成功
			unparkSuccessor(h);
		return true;
	}
	//重入锁未全部解锁
	return false;
}
/**
 *进行解锁,非重入锁更新状态标识并设置锁持有线程为null,重入锁更新锁状态标识
 *@releases 解锁状态标识state-releases(数值为1)
 */
protected final boolean tryRelease(int releases) {
	//将锁状态标识-1
	int c = getState() - releases;
	//如果当前线程不为持有锁线程进行解锁报错
	if (Thread.currentThread() != getExclusiveOwnerThread())
		throw new IllegalMonitorStateException();
	//当前线程锁是否完全解除
	boolean free = false;
	if (c == 0) {
		//当前线程锁被完全解除
		free = true;
		设置当前持有锁线程为null
		setExclusiveOwnerThread(null);
	}
    //更新锁状态标识
	setState(c);
	//返回锁是否完全释放标识
	return free;
}
/**
*初始化锁当前线程的ws,将当前线程节点的下一节点unparking解阻塞
*@node 当前线程封装节点
*/
private void unparkSuccessor(Node node) {
	//获取解锁线程的ws
	int ws = node.waitStatus;
	//将当前线程ws重新还原为0
	if (ws < 0)
		compareAndSetWaitStatus(node, ws, 0);

	/*
	 * Thread to unpark is held in successor, which is normally
	 * just the next node.  But if cancelled or apparently null,
	 * traverse backwards from tail to find the actual
	 * non-cancelled successor.
	 */
	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);
}

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值