重入锁ui
(1)重进入:spa
1.定义:重进入是指任意线程在获取到锁以后,再次获取该锁而不会被该锁所阻塞。关联一个线程持有者+计数器,重入意味着锁操做的颗粒度为“线程”。线程
2.须要解决两个问题:code
线程再次获取锁:锁须要识别获取锁的现场是否为当前占据锁的线程,若是是,则再次成功获取;对象
锁的最终释放:线程重复n次获取锁,随后在第n次释放该锁后,其余线程可以获取该锁。要求对锁对于获取进行次数的自增,计数器对当前锁被重复获取的次数进行统计,当锁被释放的时候,计数器自减,当计数器值为0时,表示锁成功释放。blog
3.重入锁实现重入性:每一个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程均可能得到该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,而且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程若是再次请求这个锁,就能够再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,若是计数器为0,则释放该锁递归
(2)ReentrantLock是的非公平类中经过组合自定义同步器来实现锁的获取与释放。队列
1 /** 2 * Sync中的nonfairTryAcquire()方法实现 3 * 这个跟公平类中的实现主要区别在于不会判断当前线程是不是等待时间最长的线程 4 **/ 5 final boolean nonfairTryAcquire(int acquires) { 6 final Thread current = Thread.currentThread(); 7 int c = getState(); 8 if (c == 0) { 9 // 跟FairSync中的主要区别,不会判断hasQueuedPredecessors() 10 if (compareAndSetState(0, acquires)) { 11 setExclusiveOwnerThread(current); 12 return true; 13 } 14 } 15 else if (current == getExclusiveOwnerThread()) { 16 int nextc = c + acquires; 17 if (nextc < 0) // overflow 18 throw new Error("Maximum lock count exceeded"); 19 setState(nextc); 20 return true; 21 } 22 return false; 23 }
nonfairTryAcquire()方法中,增长了再次获取同步状态的处理逻辑,经过判断当前线程是否为获取锁的线程来决定获取操做是否成功,若是是获取锁的线程再次请求,则将同步状态值进行增长并返回true,表示获取同步状态成功。资源
成功获取锁的现场再次获取锁,只是增长了同步状态值,要求ReentrantLock在释放同步状态时减小同步状态值。get
1 /** 2 * Sync中tryRelease() 3 **/ 4 protected final boolean tryRelease(int releases) { 5 // 修改当前锁的状态 6 // 若是一个线程递归获取了该锁(也就是state != 1), 那么c可能不等0 7 // 若是没有线程递归获取该锁,则c == 0 8 int c = getState() - releases; 9 10 // 若是锁的占有线程不等于当前正在执行释放操做的线程,则抛出异常 11 if (Thread.currentThread() != getExclusiveOwnerThread()) 12 throw new IllegalMonitorStateException(); 13 boolean free = false; 14 // c == 0,表示当前线程释放锁成功,同时表示递归获取了该锁的线程已经执行完毕 15 // 则设置当前锁状态为free,同时设置锁的当前线程为null,可让其余线程来获取 16 // 同时也说明,若是c != 0,则表示线程递归占用了锁资源, 17 // 因此锁的当前占用线程依然是当前释放锁的线程(实际没有释放) 18 if (c == 0) { 19 free = true; 20 setExclusiveOwnerThread(null); 21 } 22 // 从新设置锁的占有数 23 setState(c); 24 return free; 25 }
若是该锁被获取n次,则前(n-1)次tryRelease(int releases)方法必须返回false,而只有同步状态彻底释放了,才返回true,该方法将同步状态是否为0做为最终释放的条件,当同步状态为0时,将占有线程设置为null,并返回true,表示释放成功。
对于公平锁而言
1 /** 2 * FairSync中tryAcquire()的实现 3 * 返回 4 * true: 获取锁成功 5 * false: 获取锁不成功 6 **/ 7 protected final boolean tryAcquire(int acquires) { 8 // 获取当前线程 9 final Thread current = Thread.currentThread(); 10 // 获取锁资源的状态 11 // 0: 说明当前锁可当即获取,在此种状态下(又是公平锁) 12 // >0而且当前线程与持有锁资源的线程是同一个线程则state + 1并返回true 13 // >0而且占有锁资源的不是当前线程,则返回false表示获取不成功 14 int c = getState(); 15 if (c == 0) { 16 // 在锁能够当即获取的状况下 17 // 首先判断线程是不是刚刚释放锁资源的头节点的下一个节点(线程的等待前后顺序) 18 // 若是是等待时间最长的才会立刻获取到锁资源,不然不会(这也是公平与不公平的主要区别所在) 19 if (!hasQueuedPredecessors() && 20 compareAndSetState(0, acquires)) { 21 setExclusiveOwnerThread(current); 22 return true; 23 } 24 } 25 else if (current == getExclusiveOwnerThread()) { //线程能够递归获取锁 26 int nextc = c + acquires; 27 // 超过int上限值抛出错误 28 if (nextc < 0) 29 throw new Error("Maximum lock count exceeded"); 30 setState(nextc); 31 return true; 32 } 33 return false; 34 }
与非公平惟一的区别是判断条件中多了hasQueuedPredecessors()方法,即加入了同步队列中当前节点是否有前驱节点的判断,若是该方法返回了true,则表示有线程比当前线程更早地请求获取锁,因此须要等待前驱线程获取并释放锁后才能继续获取该锁。
可是非公平锁是默认实现:非公平性锁可能使线程“饥饿”,可是极少的线程切换,能够保证其更大的吞吐量。而公平性锁,保证了锁的获取按照FIFO原则,代价是进行大量的线程切换。
(3)synchronized可重入性
同一线程在调用本身类中其余synchronized方法/块或调用父类的synchronized方法/块都不会阻碍该线程的执行,就是说同一线程对同一个对象锁是可重入的,并且同一个线程能够获取同一把锁屡次,也就是能够屡次重入。