一,官方描述
关于ReentrantLock的官方描述,英文的就不贴出来了,这里我只贴出我自己翻译的描述:
reentrant是一个跟synchronized具有相同行为和语义的持有锁来访问方法和语句的互斥锁,但是reentrant还拥有被扩展的能力。
ReentrantLock会被线程拥有并且持续锁定,不会解锁。线程调用lock()方法返回后,则成功持有锁,否则这个锁正在被另一个线程所持有,只能等待另一个线程释放锁,如果当前线程拥有了锁,则调用lock()方法会立即返回,这个状态可以通过isHeldByCurrentThread和getHoldCount方法。
这个类的构造器可以接受一个可选的参数,如果被设置为true,锁会准许线程等待最长的时间。另外,锁不会保证特别的访问顺序。程序被许多线程通过公平锁访问可能会降低整体的吞吐量,而不是使用默认的设置,而且有更小的差异及时获得锁以及确保不会存在饥饿。注意,无论如何,锁的公平性不保证线程计划的公平性。因此,许多线程使用公平锁也许在其他活动线程没有处理并且自己没有持有该锁的时候,其中的一个线程会接连的多次获得这个锁。还有要注意,不定期的tryLock方法不会遵守公平设置,如果这个锁是有效的甚至如果其他线程都在等待,他也将成功获得锁。
强烈建议在实践中总是在try块中立即调用lock方法,最典型的before/after结构如下:
class X {
private final ReentrantLock lock = new ReentrantLock();
public void run() {
lock.lock();
try {
} finally {
lock.unlock();
}
}
}
二,源码分析
查看源码会发现,该类中主要又三个静态类来控制该类的功能,分别是Sync(超类),NonfairSync类,FairSync类,其中NonfairSync类是非公平类,它的作用是在有线程请求锁时,并不保证首先请求的线程可以拿到这个锁,而FairSync类是公平类,对于这个公平类,如果已经有线程持有锁了,那么请求线程会被放入队列中,然后按顺训请求锁。
在FairSync类的tryAcquire方法中,有一个if判断isFirst,这就是判断当前请求线程是否为队列中的第一个线程,如果是,则获取锁,如果不是,则跳过。
两者都有lock方法,但是实现却有一些不同,对于NonfairSync,其实现如下:
final void lock() {
if (compareAndSetState(0, 1)) //关键在于这里。这里的意思是说,如果当前锁的状态如果是0,则就给这个线程锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
FairSync类的代码如下:
final void lock() {
acquire(1); //这里就是要通过队列来给线程分配锁
}
三,总结
大多书情况下,大家可能都会选择使用synchronized来加锁,ReentrantLock确实是一种高级加锁工具,在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。
四,例子
package test;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
private static final ReentrantLock lock = new ReentrantLock();
private static final ReentrantLock fairlock = new ReentrantLock(true);
private int n;
public static void main(String[] args) {
ReentrantLockTest rlt = new ReentrantLockTest();
for(int i = 0; i < 100; i++) {
Thread nonT = new Thread(new NonFairTestThread(rlt));
nonT.setName("nonFair[" + (i + 1) + "]");
nonT.start();
Thread fairT = new Thread(new FairTestThread(rlt));
fairT.setName("fair[" + (i + 1) + "]");
fairT.start();
}
}
//非公平锁
static class NonFairTestThread implements Runnable {
private ReentrantLockTest rlt;
public NonFairTestThread(ReentrantLockTest rlt) {
this.rlt = rlt;
}
public void run() {
lock.lock();
try {
rlt.setNum(rlt.getNum() + 1);
System.out.println(Thread.currentThread().getName() + " nonfairlock***************" + rlt.getNum());
} finally {
lock.unlock();
}
}
}
//公平锁
static class FairTestThread implements Runnable {
private ReentrantLockTest rlt;
public FairTestThread(ReentrantLockTest rlt) {
this.rlt = rlt;
}
public void run() {
fairlock.lock();
try {
rlt.setNum(rlt.getNum() + 1);
System.out.println(Thread.currentThread().getName() + " fairlock=======" + rlt.getNum() +
" " + fairlock.getHoldCount() + " queuelength=" + fairlock.getQueueLength());
} finally {
fairlock.unlock();
}
}
}
public void setNum(int n) {
this.n = n;
}
public int getNum() {
return n;
}
}