一直在用concurrent包里的东西,最近想研究一下个中细节,先从ReentrantLock提供的集中获取锁的方式开始吧。
简单介绍一下ReentrantLock,可重入锁,互斥锁,提供了fair和unfair两种模式的锁。默认构造函数是unfair的锁,如果初始化时传入true的参数则会返回fair锁。所谓不公平就是在锁可获取时,不用考虑该锁队列是否有其他waiter,直接获取;反之,对于公平锁来讲就是当等待的锁资源可获取时要看下等待队列中当前线程是不是head线程,如果不是则不获取。
简单的看一下获取锁的代码:
//fair lock tryAcquire
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//unfair lock nonfairTryAcquire
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
从上面的代码块可以清晰的看到,公平锁fairLock在获取之前会看下自己是不是没有前继元素。而非公平锁unfairLock并不会。
ReentrantLock提供了lock()、tryLock()、tryLock(long timeout, TimeUnit unit)、lock.lockInterruptibly()
1)lock()
public void lock() {
sync.lock();
}
小结:该种方式获取锁不可中断,如果获取不到则一直休眠等待。
2)tryLock()
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 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;
}
3)tryLock(long timeout, TimeUnit unit)
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
4)lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
小结:lockInterruptibly()获取锁是以排他的模式获取,一旦被中断就放弃等待获取。在等待开始时首先检测中断状态,然后至少调用一次tryAcquire,成功获取就返回true。否则当前线程就开始排队,并且不断的被blocking、unblocking、invoking tryAcquire 直到获取成功或者被中断为止。
下面提供三个demo分别使用lock(), tryLock()的方式获取锁资源,示例展示了持续等待,立即返回,限时等待,中断等场景的使用。
1.如果当前获得锁的线程在做大量耗时的工作,使用lock.lock()方法申请锁的线程会一直阻塞,这样就降低了多线程的效率。而使用tryLock()方法申请锁,如果锁不可用则线程不会阻塞,转而可以去做其他工作。代码实例如下:
public class TestLockAndTryLock {
private ReentrantLock rlock = new ReentrantLock();
private void lockTest(){
long currentTime = System.currentTimeMillis();
try {
rlock.lock();
while (System.currentTimeMillis() - currentTime <= 1000){
//assume do something
}
System.out.println("lockTest----current thread get the lock: " + Thread.currentThread().getName());
}finally {
rlock.unlock();
System.out.println("lockTest----current thread release the lock: " + Thread.currentThread().getName());
}
}
private void tryLockTest(){
long currentTime = System.currentTimeMillis();
while (System.currentTimeMillis() - currentTime <= 100){
//assume do something
}
if (rlock.tryLock()){
try {
System.out.println("tryLockTest----current thread get the lock: " + Thread.currentThread().getName());
}finally {
rlock.unlock();
System.out.println("tryLockTest----current thread release the lock: " + Thread.currentThread().getName());
}
}else {
System.out.println("tryLockTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
}
}
public static void main(String[] args){
TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();
Thread lockThread = new Thread(
() -> lockAndTryLock.lockTest(), "Lock-Thread" );
Thread tryLockThread = new Thread(
() -> lockAndTryLock.tryLockTest(), "TryLock-Thread" );
tryLockThread.start();
lockThread.start();
}
}
output:
tryLockTest----current thread CAN NOT get the lock: TryLock-Thread
lockTest----current thread get the lock: Lock-Thread
lockTest----current thread release the lock: Lock-Thread
lock方法不能被中断。如果一个线程在等待获得一个锁时被中断,中断线程在获得锁之前会一直处于 阻塞状态。如果出现死锁,那么lock方法就无法被终止。但是tryLock(long,TimeUnit)在等待超时之后可以结束等待。demo如下:
public class TestLockAndTryLock {
private ReentrantLock rlock = new ReentrantLock();
private void lockTest(){
long currentTime = System.currentTimeMillis();
try {
rlock.lock();
System.out.println("lockTest----current thread get the lock: " + Thread.currentThread().getName());
while (System.currentTimeMillis() - currentTime <= 5000){
//assume do something
}
}finally {
rlock.unlock();
System.out.println("lockTest----current thread release the lock: " + Thread.currentThread().getName());
}
}
private void tryLockTest(){
long currentTime = System.currentTimeMillis();
while (System.currentTimeMillis() - currentTime <= 100){
//assume do something
}
if (rlock.tryLock()){
try {
System.out.println("tryLockTest----current thread get the lock: " + Thread.currentThread().getName());
}finally {
rlock.unlock();
System.out.println("tryLockTest----current thread release the lock: " + Thread.currentThread().getName());
}
}else {
System.out.println("tryLockTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
}
}
private void tryLockInterruptTest(){
long currentTime = System.currentTimeMillis();
while (System.currentTimeMillis() - currentTime <= 100){
//assume do something
}
try {
System.out.println("Begin time: " + System.currentTimeMillis());
if (rlock.tryLock(1, TimeUnit.SECONDS)){
try {
System.out.println("tryLockInterruptTest----current thread get the lock: " + Thread.currentThread().getName());
}finally {
rlock.unlock();
System.out.println("tryLockInterruptTest----current thread release the lock: " + Thread.currentThread().getName());
}
}else {
System.out.println("End time: " + System.currentTimeMillis());
System.out.println("tryLockInterruptTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();
Thread lockThread = new Thread(
() -> lockAndTryLock.lockTest(), "Lock-Thread" );
Thread tryLockInterruptThread = new Thread(
() -> lockAndTryLock.tryLockInterruptTest(), "TryLockInterrupt-Thread"
);
tryLockInterruptThread.start();
lockThread.start();
}
}
output:
lockTest----current thread get the lock: Lock-Thread
Begin time: 1533636472680
End time: 1533636473681
tryLockInterruptTest----current thread CAN NOT get the lock: TryLockInterrupt-Thread
lockTest----current thread release the lock: Lock-Thread
同时,tryLock(long, TimeUnit)可以被中断,demo如下:
private void tryLockInterruptTest(){
long currentTime = System.currentTimeMillis();
while (System.currentTimeMillis() - currentTime <= 100){
//assume do something
}
try {
System.out.println("Begin time: " + System.currentTimeMillis());
if (rlock.tryLock(3, TimeUnit.SECONDS)){
try {
System.out.println("tryLockInterruptTest----current thread get the lock: " + Thread.currentThread().getName());
}finally {
rlock.unlock();
System.out.println("tryLockInterruptTest----current thread release the lock: " + Thread.currentThread().getName());
}
}else {
System.out.println("End time: " + System.currentTimeMillis());
System.out.println("tryLockInterruptTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
System.out.println("tryLockInterruptTest Interrupt----current thread is interrupted: " + Thread.currentThread().getName());
}
}
public static void main(String[] args){
TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();
Thread lockThread = new Thread(
() -> lockAndTryLock.lockTest(), "Lock-Thread" );
Thread tryLockInterruptThread = new Thread(
() -> lockAndTryLock.tryLockInterruptTest(), "TryLockInterrupt-Thread"
);
tryLockInterruptThread.start();
lockThread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "is interrupted now. ");
}
tryLockInterruptThread.interrupt();
}
output:
lockTest----current thread get the lock: Lock-Thread
Begin time: 1533637530378
tryLockInterruptTest Interrupt----current thread is interrupted: TryLockInterrupt-Thread
lockTest----current thread release the lock: Lock-Thread
很明显被中断了,没有完成等待。
以上是对ReentrantLock的几种获取锁的方法的详解,并附以demo示例,如有疑问可以留言讨论。
refer:https://blog.csdn.net/u011784767/article/details/51659701