ReentrantLock
1 概括 采用synchronized进行加锁,是由jvm内部实现的 称为:内置锁。从java1.5开始,jdk api引入了新锁API 他们都继承自Lock,称为:显式锁 。比如今天的主题ReentrantLock。之所以称做显式锁,主要有两点原因:
1、相对于内置锁是有jvm内部实现的,显式锁是在使用java api实现的,确切的说是基于AQS实现的;
2、使用Lock加锁,需要显式的加锁以及释放锁,相对于内置锁使用synchronized而言,要麻烦些。
简单来说ReentrantLock提供了两个方法:
获取锁:
1 2 3 public void lock () { sync.lock(); }
释放锁:
1 2 3 public void unlock () { sync.release(1 ); }
ReentrantLock实现机制:首先提供了一个volatile修饰的state,当state=0时表明没有线程获取锁,一个线程想要获取锁时,如果state=0,则将state=1,此时这个线程获取到了锁,其他线程来获取锁时会判断state,此时state不等于0,将此线程添加到一个FIFO队列中,并将线程阻塞,当一个线程释放锁时会通知阻塞的线程,使阻塞线程变成可执行线程来竞争锁(判断state是否是0),当一个线程已经获得锁并再次获取锁时,虽然此时state=1,此时只需要将state++就好,这样这个线程就获取了多次锁,实现机制在AbstractQueuedSynchronizer中接下来我们会对源码简单分析
简单来说锁的实现机制是通过一个volatile变量state和线程FIFO来实现的,state为0时锁可获取,线程执行;当state不为0且线程不是当前可执行线程时将线程放到FIFO队列中并阻塞,线程执行完之后再唤醒阻塞线程来进行竞争锁。
1.1 显示锁和内置锁 简单的讲,显式锁的内置锁的补充:显式锁Lock提供了中断锁、定时锁、可轮询等实现,这些都是内置锁synchronized不具备的;显式锁可以指定为公平锁或非公平锁,而内置锁synchronized锁是非公平的。使用synchronized加锁,有些情况下很容易导致死锁,在这种情况下改用显式锁定时功能 在一段时间没有获取到锁,就放弃获取锁 就可以避免死锁。
另外与内置锁还有一个明显不同的地方是,内置锁可以用在方法上,而显式锁只能用在代码块上,也就是说强制使用更细粒度的加锁。
可以说内置锁和显式锁是互补关系:显式锁不能用在方法上,而且容易忘记释放锁;内置锁不可中断,有些情况下容易产生死锁,另外内置锁无法实现公平锁。根据这些差异在自己的实际业务场景中选择性使用即可,需要说明下的是显式锁的性能比内置锁性能稍微好些。
2 ReentrantLock提供的接口
// 创建一个 ReentrantLock ,默认是“非公平锁”。 ReentrantLock() // 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。 ReentrantLock(boolean fair) // 查询当前线程保持此锁的次数。 int getHoldCount() // 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。 protected Thread getOwner() // 返回一个 collection,它包含可能正等待获取此锁的线程。 protected Collection getQueuedThreads() // 返回正等待获取此锁的线程估计数。 int getQueueLength() // 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。 protected Collection getWaitingThreads(Condition condition) // 返回等待与此锁相关的给定条件的线程估计数。 int getWaitQueueLength(Condition condition) // 查询给定线程是否正在等待获取此锁。 boolean hasQueuedThread(Thread thread) // 查询是否有些线程正在等待获取此锁。 boolean hasQueuedThreads() // 查询是否有些线程正在等待与此锁有关的给定条件。 boolean hasWaiters(Condition condition) // 如果是“公平锁”返回true,否则返回false。 boolean isFair() // 查询当前线程是否保持此锁。 boolean isHeldByCurrentThread() // 查询此锁是否由任意线程保持。 boolean isLocked() // 获取锁。 void lock() // 如果当前线程未被中断,则获取锁。 void lockInterruptibly() // 返回用来与此 Lock 实例一起使用的 Condition 实例。 Condition newCondition() // 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。 boolean tryLock() // 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。 boolean tryLock(long timeout, TimeUnit unit) // 试图释放此锁。
void unlock()
2.1 tryLock和lock和lockInterruptibly的区别
tryLock能获得锁就返回true,不能就立即返回false,tryLock(long timeout,TimeUnit unit),可以增加时间限制,如果超过该时间段还没获得锁,返回false
lock能获得锁就返回true,不能的话一直等待获得锁
lock和lockInterruptibly,如果两个线程分别执行这两个方法,但此时中断这两个线程,前者不会抛出异常,而后者会抛出异常
2.2 Lock的公平锁和非公平锁 1 2 Lock lock=new ReentrantLock(true ); Lock lock=new ReentrantLock(false );
3 ReentrantLock源码 首先需要说明的ReentrantLock与内置锁一样都是排它锁,从名字上开是与内置锁一样是可重入的。 ReentrantLock是排它锁实现,也就是说ReentrantLock的内部类在实现AQS时是对tryAcquire、tryRelease这两个方法进行扩展的。
3.1 内部类Sync对AQS的实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L ; abstract void lock () ; 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 ) throw new Error("Maximum lock count exceeded" ); setState(nextc); return true ; } return false ; } protected final boolean tryRelease (int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false ; if (c == 0 ) { free = true ; setExclusiveOwnerThread(null ); } setState(c); return free; } protected final boolean isHeldExclusively () { return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition () { return new ConditionObject(); } final Thread getOwner () { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount () { return isHeldExclusively() ? getState() : 0 ; } final boolean isLocked () { return getState() != 0 ; } private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0 ); } }
3.2 Sync子类 – 非公平锁: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L ; final void lock () { if (compareAndSetState(0 , 1 )) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1 ); } protected final boolean tryAcquire (int acquires) { return nonfairTryAcquire(acquires); } }
非公平锁的实现很简单,在lock获取锁时首先判断判断当前锁是否可以用(AQS的state状态值是否为0),如果是 直接“插队”获取锁,否则进入排队队列,并阻塞当前线程。
3.3 Sync子类 – 公平锁: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L ; final void lock () { acquire(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()) { int nextc = c + acquires; if (nextc < 0 ) throw new Error("Maximum lock count exceeded" ); setState(nextc); return true ; } return false ; } }
公平锁的实现,通过hasQueuedPredecessors方法判断当前线程是否是AQS队列中的头结点或者AQS队列为空,并且当前锁状态可用 可以直接获取锁,否则需要排队。
4 ReentrantLock示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Operation { private Lock lock; int value = 1 ; public Operation () { this .lock = new ReentrantLock(false ); } public void print1 () { lock.lock(); value = value +1 ; System.err.println(value); lock.unlock(); } public void print2 () { lock.lock(); value = value +2 ; System.err.println(value); lock.unlock(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class LockMain { public static void main (String[] args) { final Operation operation = new Operation(); Runnable runnable = new Runnable() { @Override public void run () { operation.print1(); } }; Runnable runnable2 = new Runnable() { @Override public void run () { operation.print2(); } }; Runnable runnable3 = new Runnable() { @Override public void run () { operation.print1(); } }; Thread thread = new Thread(runnable); Thread thread2 = new Thread(runnable2); Thread thread3 = new Thread(runnable3); thread.start(); thread2.start(); thread3.start(); } }