目录
4、thread.interrupt()方法能够中断线程B的等待过程
5.2、tryLock(long, TimeUnit) + 当线程.interrpt() ==》中断线程
5.3、lockInterruptibly() + 当前线程.interrupt() ==》中断线程
1、ReentrantLock
- synchronized重量级锁,可重入锁,互斥锁。JDK前期的版本lock比synchronized更快,在JDK1.5之后synchronized引入了偏向锁,轻量级锁和重量级锁。以致两种锁性能旗鼓相当。
- ReentrantLock(轻量级锁)也可以叫对象锁,可重入锁,互斥锁。提供了fair和unfair两种模式的锁。默认构造函数是unfair的锁,如果初始化时传入true的参数则会返回fair锁。
- 不公平:在锁可获取时,不用考虑该锁队列是否有其他waiter,直接获取;
- 公平锁:按照排队顺序获取锁。
2、方法说明
- lock():使用得最多的一个方法,用来获取锁。如果锁已被其他线程获取,则进行等待。
- tryLock():用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,无论如何都会立即返回。在拿不到锁时不会一直在那等待。
- tryLock(long time, TimeUnit unit):在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true,同时在等待的时候调用interrupt() 方法也会中断线程。
- lockInterruptibly():当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
- lock.unlock():判断当前线程有没有获取到锁,如果没获取到锁就解锁会抛出 new IllegalMonitorStateException();
举一个例子如下:
假如线程A
和线程B
使用同一个锁LOCK
,此时线程A首先获取到锁LOCK.lock()
,并且始终持有不释放。如果此时B要去获取锁,有四种方式:
-
LOCK.lock()
: 此方式会始终处于等待中,即使调用B.interrupt()
也不能中断,除非线程A调用LOCK.unlock()
释放锁。 -
LOCK.lockInterruptibly()
: 此方式会等待,但当调用B.interrupt()
会被中断等待,并抛出InterruptedException
异常,否则会与lock()
一样始终处于等待中,直到线程A释放锁。 -
LOCK.tryLock()
: 该处不会等待,获取不到锁并直接返回false,去执行下面的逻辑。 -
LOCK.tryLock(10, TimeUnit.SECONDS)
:该处会在10秒时间内处于等待中,但当调用B.interrupt()
会被中断等待,并抛出InterruptedException
。10秒时间内如果线程A释放锁,会获取到锁并返回true,否则10秒过后会获取不到锁并返回false,去执行下面的逻辑。
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
3、Lock和TryLock的区别
- lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。
- tryLock是可以被打断的,被中断 的,lock是不可以。
4、thread.interrupt()方法能够中断线程B的等待过程
示例:
要执行doBussiness()中的代码首先需要得到锁lock,线程使用lockInterruptibly()方法获取锁。
如下示例中,t0得到了锁,t1等待。在t1等待时,调用t1.interrupt(),中断t1的等待。
public class LockTest {
private Lock lock = new ReentrantLock();
public void doBussiness() {
String name = Thread.currentThread().getName();
try {
System.out.println(name + " 开始获取锁");
lock.lockInterruptibly();
System.out.println(name + " 得到锁");
System.out.println(name + " 开工干活");
for (int i=0; i<5; i++) {
Thread.sleep(1000);
System.out.println(name + " : " + i);
}
} catch (InterruptedException e) {
System.out.println(name + " 被中断");
System.out.println(name + " 做些别的事情");
} finally {
try {
lock.unlock();
System.out.println(name + " 释放锁");
} catch (Exception e) {
System.out.println(name + " : 没有得到锁的线程运行结束");
}
}
}
public static void main(String[] args) throws InterruptedException {
LockTest lockTest = new LockTest();
Thread t0 = new Thread(
new Runnable() {
public void run() {
lockTest.doBussiness();
}
}
);
Thread t1 = new Thread(
new Runnable() {
public void run() {
lockTest.doBussiness();
}
}
);
// 启动线程t1
t0.start();
Thread.sleep(10);
// 启动线程t2
t1.start();
Thread.sleep(100);
// 线程t1没有得到锁,中断t1的等待
t1.interrupt();
}
}
运行结果:
Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-1 被中断
Thread-1 做些别的事情
Thread-1 : 没有得到锁的线程运行结束
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-0 释放锁
如果使用lock()方法获取锁,线程不会被中断。
将
lock.lockInterruptibly();
改为
lock.lock();
运行结果如下:
Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-1 得到锁
Thread-1 开工干活
Thread-1 被中断
Thread-1 做些别的事情
Thread-1 释放锁
Thread-0 释放锁
去掉
t1.interrupt();
运行结果如下:
Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-0 释放锁
Thread-1 得到锁
Thread-1 开工干活
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 释放锁
5、获取锁的方式源码解读
5.1、lock()
public void lock() {
sync.lock();
}
- 当锁可用,并且当前线程没有持有该锁,直接获取锁并把count set为1。
- 当锁可用,并且当前线程已经持有该锁,直接获取锁并把count增加1。
- 当锁不可用,那么当前线程被阻塞,休眠一直到该锁可以获取,然后把持有count设置为1。
小结:该种方式获取锁不可中断,如果获取不到则一直休眠等待。
5.2、tryLock(long, TimeUnit) + 当线程.interrpt() ==》中断线程
5.3、lockInterruptibly() + 当前线程.interrupt() ==》中断线程
5.4、公平和非公平锁源码
//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并不会。
参考:阿里面试实战题2----ReentrantLock里面lock和tryLock的区别 - wenbochang - 博客园