目录
一、基本使用方法
ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式,以lock()为例,其使用方式为:
ReentrantLock takeLock = new ReentrantLock();
// 获取锁
takeLock.lock();
try {
// 业务逻辑
} finally {
// 释放锁
takeLock.unlock();
}
那么,ReentrantLock内部是如何实现锁的呢?接下来我们就以JDK1.7中的ReentrantLock的lock()方法叙述。
二、ReentrantLock类
ReentrantLock类实现了Lock和java.io.Serializable接口,其内部有一个实现锁功能的关键成员变量Sync类型的sync,定义如下:
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
锁类型: NonfairSync、FairSync,正好对应了ReentrantLock的非公平锁、公平锁两大类型。ReentrantLock默认是公平锁的实现。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
当然我们可以通过另一个构造方法实现兼容的方式:
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock的lock()方法会调用其内部成员变量sync的lock()方法;
其次,sync的非公平锁NonfairSync或公平锁FairSync实现了父类AbstractQueuedSynchronizer的lock()方法,其会调用acquire()方法;
然后,acquire()方法则在sync父类AbstractQueuedSynchronizer中实现,它只有一段代码:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()方法试图获取锁,获取到直接返回结果,否则就通过嵌套调用acquireQueued()、addWaiter()方法将请求获取锁的线程加入等待队列,等到成功的时候,将当前请求线程阻塞,接着就执行结束!
而针对每种锁来说,其实现方式有很大差别,主要就体现在各自实现类的lock()和tryAcquire()方法中。在sync的抽象类Sync及其抽象父类AQS中,lock()方法和tryAcquire()方法被定义为抽象方法或者未实现,而是由具体子类去实现:
- 非公平锁NonfairSync的具体实现
lock方法:
/**
* 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);
}
非公平锁上来就无视等待队列的存在而抢占锁,通过基于CAS操作的compareAndSetState(0, 1)方法,试图修改当前锁的状态,这个0表示AbstractQueuedSynchronizer内部的一种状态,针对互斥锁则是尚未有线程持有该锁,而>=1则表示存在线程持有该锁,并重入对应次数,这个上来就CAS的操作也是非公共锁的一种体现,CAS操作成功的话,则将当前线程设置为该锁的唯一拥有者。抢占不成功的话,则调用父类的acquire()方法,由上所述,继而调用tryAcquire()方法,这个方法也是由最终实现类NonfairSync实现的,如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire()方法实现如下:
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
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来抢占,抢占成功,直接返回true,如果锁的持有者线程为当前线程的话,则通过累加状态标识重入次数。抢占不成功,或者锁的本身持有者不是当前线程,则返回false,继而后续通过进入等待队列的方式排队获取锁。
- 公平锁FairSync的具体实现
lock方法:final void lock() { acquire(1); }
公平锁的tryAcquire()方法也相对较简单,如下:
/** * 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; }
当前线程会在得到当前锁状态为0,即没有线程持有该锁,并且通过!hasQueuedPredecessors()判断当前等待队列没有前继线程(也就是说,没有比我优先级更高的线程在请求锁了)获取锁的情况下,通过CAS抢占锁,并设置自己为锁的当前拥有者,当然,如果是重入的话,和非公平锁处理一样,通过累加状态位标记重入次数。
而一旦等待队列中有等待者,或当前线程抢占锁失败,则它会乖乖的进入等待队列排队等待。
三、其他注意
即便是公平锁,如果通过不带超时时间限制的tryLock()的方式获取锁的话,它也是不公平的,因为其内部调用的是sync.nonfairTryAcquire()方法,无论抢到与否,都会同步返回。如下:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
但是带有超时时间限制的tryLock(long timeout, TimeUnit unit)方法则不一样,还是会遵循公平或非公平的原则的,如下:
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
四、应用与实现(生产者消费者模式)
1、先定义一个仓库接口,用于消费和存储:
/**
* Created by fx on 2019/1/15.
*/
public interface StoreInterface {
void produce(int number);
void consume(int number);
}
2、实现消费工厂的消费功能和生成工厂的生成功能,并加上锁:
当消费者消费的数量不足时,此时仓库是不可以支出库存的,所以用到reentrantLock.lock(),等待生产者的生产,执行condition.wait()等待。循环等待,当库存量大于需求量,则唤醒当前的消费者,condition.signalAll(),这里也可以用两个condition分别控制消费者和生产者,这里为了方便,只用一个condition控制两个的执行,但是一个condition只能针对于一个线程任务。
生产者同理,当生产量大于仓库容纳总量就会等待,停止生产,知道消费者消费到一定的库存量,让他能继续生产放入仓库。
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by fx on 2019/1/15.
*/
public class ReentrantlockStore implements StoreInterface{
private LinkedList linkList = new LinkedList();
private static int Max_Value = 100;
private int number;
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition condition = reentrantLock.newCondition();
public void produce(int number){
reentrantLock.lock();
try{
while(linkList.size()+number>Max_Value){
System.out.println("【要生产的产品数量】:" + number + "\t【库存量】:"+ linkList.size() + "\t暂时不能执行生产任务!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
for (int i=0;i<number;i++ ){
linkList.add(new Object());
}
System.out.println("【已经生产产品数】:" + number + "\t【现仓储量为】:" + linkList.size());
condition.signalAll();
}
public void consume(int number){
reentrantLock.lock();
try{
while (linkList.size()<number){
System.out.println("【要消费的产品数量】:" + number + "\t【库存量】:"+ linkList.size() + "\t暂时不能执行消费任务!");
try {
condition.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
for (int i=0;i<number;i++ ){
linkList.remove();
}
System.out.println("【已经消费产品数】:" + number + "\t【现仓储量为】:" + linkList.size());
condition.signalAll();
}
}
3、定义消费者工厂:
/**
* Created by fx on 2019/1/15.
*/
public class ConsumerFactory extends Thread {
private int number;
private StoreInterface storeHouse;
public ConsumerFactory(int number,StoreInterface storeHouse){
this.number =number;
this.storeHouse = storeHouse;
}
public void consume(int number){
storeHouse.consume(number);
}
@Override
public void run() {
consume(number);
}
}
4、定义生成者工厂:
/**
* Created by fx on 2019/1/15.
*/
public class ProductFactory extends Thread{
private int number;
private StoreInterface storeHouse;
public ProductFactory(int number,StoreInterface storeHouse){
this.number = number;
this.storeHouse = storeHouse;
}
@Override
public void run() {
produce(number);
}
public void produce(int number){
storeHouse.produce(number);
}
}
5、最后的执行主函数,来确定消费者和生产者:这里定义了十个线程,消费者每次消费五个,生产者每次生产三个,库存总量是最大100.
/**
* Created by fx on 2019/1/15.
*/
public class MainDemo {
public static void main(String[] args) {
StoreHouse storeHouse = new StoreHouse();
for(int i =0;i<10;i++){
ThreadPoolExecutorUtil.threadPool.submit(new ConsumerFactory(5,storeHouse));
ThreadPoolExecutorUtil.threadPool.submit(new ProductFactory(3,storeHouse));
}
System.out.println("**************分割线****************");
ReentrantlockStore reentrantlockStore = new ReentrantlockStore();
for(int i =0;i<10;i++){
ThreadPoolExecutorUtil.threadPool.submit(new ConsumerFactory(5,reentrantlockStore));
ThreadPoolExecutorUtil.threadPool.submit(new ProductFactory(3,reentrantlockStore));
}
}
}
最后结果显示:
以上就是Reentrantlock的基本讲解和用法,想了解更多,可以查看源码。当然也要记得看他的父类AQS哦,这里也是相当有趣呢。有时间我也会写一篇关于AQS的讲解。