一.问题
ReentrantLock是jdk5里面的一个方法类,
主要讨论两个问题:
1.ReentrantLock的可重入体现
2.ReentrantLock的公平锁和非公平锁体现
二.讲解
2.1ReentrantLock的可重入体现
final boolean nonfairTryAcquire(int acquires) { //这里我就跳过着讲解了,ReentrantLock模式使用的是非公平锁,这样能提高系统的响应性能 final Thread current = Thread.currentThread(); int c = getState(); //获取资源的状态, if (c == 0) { //为0就是别人还没有获取到锁,这个时候当前线程就可以获取到锁 if (compareAndSetState(0, acquires)) { //用cas的方式获取到锁 setExclusiveOwnerThread(current); //这个方法里面就只有这一句 exclusiveOwnerThread = thread; 设置当前线程是独占线程 return true; } } else if (current == getExclusiveOwnerThread()) { //重点来了,这个方法就是主要判断是不是可重入的,如果之前的判断资源的状态是被上锁了,就会执行到这里,如果判断是本线程 int nextc = c + acquires; //把资源的的请求次数加1 if (nextc < 0) // 当然也不是可以不限加的,如果超出的int的范围,抛出一个error的错误 throw new Error("Maximum lock count exceeded"); setState(nextc); //设置资源状态 return true; } return false; }
总结:ReentrantLock可重入主要体现在current == getExclusiveOwnerThread()这个判断方法上面。如果是当前重入线程,资源状态添加请求数,注意释放的时候也是要释放这个多次的。
2.2 ReentrantLock的公平锁和非公平锁体现
public ReentrantLock(boolean fair) { //这个构造方法是用来生成公平锁和非公平锁的 fair=true 公平锁,false 非公平锁 sync = fair ? new FairSync() : new NonfairSync(); }
补:公平锁:多个线程按fifo(先进先出)的顺序获取资源。典型的例子就是排队买票。
非公平锁:多个线程不是按顺序获取资源的。典型的例子就是插队买票。
非公平锁的上锁过程
final void lock() { if (compareAndSetState(0, 1)) //如果当前这个线程可以用cas快获取得到资源的话,那就优先设置当前线程占用资源,这个是不管什么顺序的 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); //快速设置资源失败,还是老老实实的去获取请求的资源吧 acquire 会调用nonfairTryAcquire方法 }
上面已经讲解了nonfairTryAcquire方法 ,这里不再重复讲解
公平锁的上锁过程
final void lock() { acquire(1); //很简单 就是当前线程要一个资源 }
protected final boolean tryAcquire(int acquires) { //这里是公平锁的获取过程 final Thread current = Thread.currentThread(); int c = getState(); //获取当前线程的状态,如果是0说明还没有别的线程占用资源 if (c == 0) { if (!hasQueuedPredecessors() && //公平锁的重点,里面用了队列来实现锁,如果队列中第一个为空或者自己是第一个,就自己再去获取资源,如果有值了,就判断一个自己之前是不是已经获取过资源了,获取了就在重入一下,重入失败就返回false,表示获取资源失败 compareAndSetState(0, acquires)) { //下面和nonfairTryAcquire里面的一样,不做重复讲解 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; } }
线面来重点讲讲hasQueuedPredecessors()方法
public final boolean hasQueuedPredecessors() { //这个方法用来判断是不是队列有前驱 Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); //队列不为空的情况下&&(队列中第一个有值||第一个线程不是自己) }
!hasQueuedPredecessors()这样方法说人话的意思就是
要么这个队列没有值,要么有值也就只能有我一个!
总结:公平锁的获取方式和非公平锁的获取方式有两点特殊的地方
①.公平锁不支持快速获取资源而非公平锁支持,(非公平锁获取到了,代码就不往下执行了,效率高于公平锁,这样的说服力够明显吧)
②.公平锁在获取资源的时候会判断当前AQS队列里面是不是有线程,还有就是当前线程要是获取资源的第一个。
非公平锁就无所谓了,谁获取锁都行!
ReentrantLock的特性还有很多,未完后续再补...........