lock,tryLock 和 lockInterruptibly 是 Java ReentrantLock 对象提供的三种获取锁的方式。
某个线程调用ReentrantLock 对象的 lock 方法后,如果锁尚未被其它线程占有,则该线程将获得锁;如果锁已经被其它线程占有,则该线程将一直 block 直到它成功获得锁。block 过程中不响应中断,获得锁后才根据中断状态进行相应处理。
某个线程调用ReentrantLock 对象的 lockInterruptibly 方法后,如果锁尚未被其它线程占有,则该线程将获得锁;如果锁已经被其它线程占有,则该线程将一直 block 直到它成功获得锁或者该线程被中断。block 过程中发生线程中断时,该线程退出锁的竞争,抛出 InterruptedException 异常。
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock 的 lock() 方法和 lockInterruptibly() 方法的差异测试
*/
public class LockInterruptiblyTest {
/**
* 在 lockNoInterrupt 上使用 lock() 方法
*/
ReentrantLock lockNoInterrupt = new ReentrantLock();
public void noInterruptLock(){
Runnable noInterruptLock = () -> {
lockNoInterrupt.lock();
try{
System.out.println(String.format("%s %s locked", new Date(System.currentTimeMillis()), Thread.currentThread().getName()));
}finally{
//这里不释放锁,测试锁竞争者如何处理中断
//lockNoInterrupt.unlock();
}
};
//lockNoInterrupt 将被下面的两个线程竞争
Thread noInterruptThread1 = new Thread(noInterruptLock, "No Interrupt Thread 1");
Thread noInterruptThread2 = new Thread(noInterruptLock, "No Interrupt Thread 2");
//No Interrupt Thread 1 获取锁,并且不释放
noInterruptThread1.start();
try{
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//No Interrupt Thread 2 尝试获取锁,会被 block
noInterruptThread2.start();
try{
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//No Interrupt Thread 2 无法得到锁,尝试中断,但中断不会被线程响应
noInterruptThread2.interrupt();
}
/**
* 在 lockInterrupt 上使用 lockInterruptibly() 方法
*/
ReentrantLock lockInterrupt = new ReentrantLock();
public void lockInterrupt(){
//测试 lockInterruptibly() 方法
Runnable interruptLock = () -> {
try{
lockInterrupt.lockInterruptibly();
System.out.println(String.format("%s %s locked", new Date(System.currentTimeMillis()), Thread.currentThread().getName()));
}catch (InterruptedException e) {
System.out.println(String.format("%s %s interrupted", new Date(System.currentTimeMillis()), Thread.currentThread().getName()));
}finally{
//这里不释放锁,测试锁竞争者如何处理中断
//lockInterrupt.unlock();
}
};
//lockInterrupt 将被下面的两个线程竞争
Thread interruptThread1 = new Thread(interruptLock, "Interrupt Thread 1");
Thread interruptThread2 = new Thread(interruptLock, "Interrupt Thread 2");
//Interrupt Thread 1 获取锁,并且不释放
interruptThread1.start();
try{
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Interrupt Thread 2 尝试获取锁,会被 block
interruptThread2.start();
try{
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Interrupt Thread 2 无法得到锁,尝试中断,线程中断后结束
interruptThread2.interrupt();
}
public static void main(String[] args){
LockInterruptiblyTest test = new LockInterruptiblyTest();
test.noInterruptLock();
test.lockInterrupt();
}
}
/* output
Tue Dec 04 18:45:30 CST 2018 No Interrupt Thread 1 locked
Tue Dec 04 18:45:32 CST 2018 Interrupt Thread 1 locked
Tue Dec 04 18:45:34 CST 2018 Interrupt Thread 2 interrupted
...
*/
我们发现 No Interrupt Thread 1 和 No Interrupt Thread 2 使用了 lock 方法,由于 No Interrupt Thread 1 一直占有锁,No Interrupt Thread 2 在尝试获得锁后会一直 block,即使尝试中断它,No Interrupt Thread 2 线程仍保持 wait 锁的状态,程序无法结束。
而 Interrupt Thread 1 和 Interrupt Thread 2 使用了 lockInterruptibly 方法,由于 Interrupt Thread 1 一直占有锁,Interrupt Thread 2 在尝试获得锁后会一直 block,但我们尝试中断它,Interrupt Thread 2 线程就会停止 wait 锁,处理中断请求,进而线程结束。