重入锁ReentrantLock

ReentrantLock重入锁

​ 使用ReentrantLock可以完成和synchronized同样的功能,但是使用synchronized可以自动释放锁,使用重入锁必须要手动释放锁,所以一般会放在finally块中释放锁。

尝试锁定

​ 使用ReentrantLock可以进行尝试锁定,这样在无法锁定或者在指定时间内无法锁定,线程可以决定是否继续等待。在尝试锁定之后,不管有没有锁定,方法都将继续执行。所以,可以在方法中根据是否锁定来进行相应的逻辑处理。

获取不到锁可被interrupt

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
public class Lock02 {

Lock lock = new ReentrantLock();

void m1 () {
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
} catch (InterruptedException e) {
System.out.println("t1 interrupted");
} finally {
lock.unlock();
}
}

void m2() {
boolean isLock = false;
try {
// lock.lock();
lock.lockInterruptibly();
isLock = lock.tryLock();
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (Exception e) {
System.out.println("t2 interrupted");
} finally {
if (isLock) {
lock.unlock();
}

}
}

public static void main(String[] args) {
Lock02 lock01 = new Lock02();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lock01.m1();
}
});
t1.start();

Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
lock01.m2();
}
});

t2.start();

try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();
}
/**
控制台输出:
t1 start
t2 interrupted
*/

}

​ 线程t1在Integer.MAX_VALUE的时间内会一直执行,正常情况下t2是获取不到锁的,这个时候如果不做处理,那么t2将会一直等待下去,不会被其他线程打断。但是如果在t2中调用了lock.lockInterruptibly();方法之后,则表明t2是可以被打断的。也就是说,t2在等待t1释放锁的过程中,如果不想继续等了,主线程调用t2.interrupt()方法即可将t2线程打断。

指定为公平锁

​ 假如现在有6个线程,其中一个线程拿到了锁顺利执行完逻辑后释放了锁,这时候剩下的5个线程都会去争用这把锁,谁先拿到就是谁的,谁就可以执行,并不会关心其他的线程等了多长的时间,也就是说不会根据谁等的时间长,谁就优先获得锁。这种称为竞争锁,不是公平锁。这种的效率会高些,因为线程调度器不用计算哪个线程等待的时间更长。synchronized默认是非公平锁。

​ 公平锁自然就是根据等待时间来判断谁可以先获取到锁的。new ReentrantLock(true)即可声明为公平锁。