0%

Java中的ReentrantLock

Java中的ReentrantLock基本功能与synchronized代码块相当,但是提供了一些高级特性。

首先来看一下ReentrantLock的基本使用:

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

ReentrantLock mLock = new ReentrantLock();

int num = 0;

Runnable mRunnableOne = new Runnable() {
@Override
public void run() {
mLock.lock();
System.out.println("ThreadOne: start");
try {
for (int m = 0; m < Integer.MAX_VALUE; m++) {
num++;
}
System.out.println("ThreadOne: over");
} finally {
mLock.unlock();
}
}
};

Runnable mRunnableTwo = new Runnable() {
@Override
public void run() {
mLock.lock();
System.out.println(num);
mLock.unlock();
}
};

public static void main(String[] args) {
LockTest test = new LockTest();
Thread a = new Thread(test.mRunnableOne);
Thread b = new Thread(test.mRunnableTwo);

a.start();
b.start();
}
}

在这段程序中,我们定义了两个Runnable,第一个线程获得锁,循环将变量num值+1,第二个线程尝试获得锁,并打印num的值。
运行这段程序,会输出:

1
2
3
ThreadOne: start
ThreadOne: over
2147483647

可以看到线程2一开始无法获得锁,需等待线程1执行完毕后,才可打印num的数值。

下面来看看ReentrantLock的一些特性。
1.ReentrantLock允许同一个线程多次调用lock接口获取锁,每调用一次计数便加一。因此在释放锁的时候必须调用相应多次数unlock才能释放锁:

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
package study.threadtest;

import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
......

Runnable mRunnableOne = new Runnable() {
@Override
public void run() {
mLock.lock();
mLock.lock();
System.out.println("ThreadOne: start");
try {
for (int m = 0; m < Integer.MAX_VALUE; m++) {
num++;
}
System.out.println("ThreadOne: over");
} finally {
mLock.unlock();
}
}
};
......

}

我们对线程一做一下修改,多调用一次lock接口。程序运行后输出:

1
2
ThreadOne: start
ThreadOne: over

可见由于调用unlock次数不够,线程2并不能获取到锁,因此无法输出打印。

2.synchronized在获取不到锁的时候会一直阻塞,直到这个线程获取到锁才继续执行。由上面的例子可以看到ReentrantLock的lock接口也是这样的,旦ReentrantLock同时还提供了一个tryLock接口,它允许线程尝试获取锁,如果无法获得锁,就执行其他的逻辑,而不是一直阻塞等待。

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

ReentrantLock mLock = new ReentrantLock();

int num = 0;

Runnable mRunnableOne = new Runnable() {
@Override
public void run() {
mLock.lock();
System.out.println("ThreadOne: start");
try {
for (int m = 0; m < Integer.MAX_VALUE; m++) {
num++;
}
System.out.println("ThreadOne: over");
} finally {
mLock.unlock();
}
}
};

Runnable mRunnableTwo = new Runnable() {
@Override
public void run() {
if (mLock.tryLock()) {
System.out.println(num);
mLock.unlock();
} else {
System.out.println("ThreadTwo: Do something else");
}
}
};

public static void main(String[] args) {
LockTest test = new LockTest();
Thread a = new Thread(test.mRunnableOne);
Thread b = new Thread(test.mRunnableTwo);

a.start();
b.start();
}
}

我们在线程2中使用tryLock尝试获取锁,如果不能获取,则执行else中的逻辑。运行代码可以看到输出:

1
2
3
ThreadOne: start
ThreadTwo: Do something else
ThreadOne: over