第4章 Lock的使用
4.1 重入锁
所谓重入锁,就是可以重新进入的锁。。废话!反正我用纯文字解释不了他是什么意思。还是举个例子解释它的概念吧。
假如有n个线程,都想操作共享变量obj,现在假设线程A的method1()
通过lock.lock()
拿到了锁,在释放锁之前method1()
又调用了method2()
,method2()
开头第一句就是lock.lock()
尝试去获得锁。在这种情况下,method1()
调用method2()
使得method2()
中代码
得以成功执行。也就是Re-Entrance的意思,即所谓重入锁。
BTW,重入锁默认是非公平锁。
重入锁有三个获得相关线程数目的方法:
重入锁底层采用的是计数的方式,已经获得锁的线程每调用一次
lock()
,计数器+1,相反每调用一次unlock()
,计数器-1。可以通过lock.getHoldCount()
获得当前计数器的值。上述针对的都是已经获得锁的线程而言的。我们知道,
Thread.sleep()
函数会让当前线程等待,且与wait()
最大的区别就是不会释放锁。现在假设有n个线程,其中有一个拿到了锁,但是中间因为某些原因,被无限地睡眠了。
这个时候,其他n-1个线程又在等这个锁释放,可以通过lock.getQueueLength()
获得当前正在等待锁的线程数。lock.getWaitQueueLength()
和lock.getQueueLength()
很相似,区别是前者返回的是等待与此锁相关的给定Condition对象的线程数。如果有5个线程,每个线程都调用了Condition对象cond的await()方法,则返回5.
4.1.1 重入锁实现同步
Java中出了用synchronized
关键字实现同步,也可以用ReentrantLock
对象来实现。
可以把synchronized同步的那个对象看做是一个重入锁。
1 | package ch04; |
4.1.2 重入锁实现wait/notify
在等待/通知机制中,我们通过调用obj.wait()
方法,使得当前线程(这里的当前线程指的是当前占用了用CPU的线程)等待obj
完成。
之前在第三章说过,在调用wait()、notify()、notifyAll()
方法前必须得到对象锁,也就是说,要判断共享变量obj是否到了不再等待的状态,我们必须先拿到这个共享变量obj的锁。
在锁的世界中,等待某个共享变量是否达到一个状态叫Condition
。在Condition对象cond.await()
或cond.notify()
之前,必须获得对象监视器
,也就是我们说的锁。
扯了这么多,看个代码就知道了: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
69package ch04;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName ReentrantLockWaitNotifyTest
* @Deacription // TODO
* @Author LiuZhian
* @Date 2019-11-09 16:17
* @Version 1.0
**/
public class ReentrantLockWaitNotifyTest {
public static void main(String[] args) {
MyServ serv = new MyServ();
TestThreadA tA = new TestThreadA(serv);
tA.start();
// 这里main 线程等2秒,目的是防止在await之前signal
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
serv.signalMethod();
}
}
class MyServ {
private ReentrantLock lock = new ReentrantLock();
private Condition cond = lock.newCondition();
public void waitMethod() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁,开始等待!");
try {
cond.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放锁!");
}
}
public void signalMethod()
{
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁,开始通知!");
cond.signal();
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放锁,通知完毕!");
}
}
class TestThreadA extends Thread {
private MyServ serv;
public TestThreadA(MyServ serv) {
this.serv = serv;
}
public void run() {
super.run();
serv.waitMethod();
}
}
4.1.3 重入锁实现 消费者/生产者问题
上面提到的Condition,其实并不是我们通常说的true/false这样的condition,而是同步互斥中的竞争条件,是一种抽象的概念,我更喜欢把它理解为控制进程等待状态的一个等待条件。
举生产者/消费者的例子来说,我们真的需要一个T/F条件来控制wait和notify。代码如下: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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90package ch04;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName ReentrantLockPC
* @Deacription // TODO
* @Author LiuZhian
* @Date 2019-11-09 16:43
* @Version 1.0
**/
public class ReentrantLockPCTest {
public static void main(String[] args) {
MyProducerConsumer mpc = new MyProducerConsumer();
MyP p = new MyP(mpc);
MyC c = new MyC(mpc);
p.start();
c.start();
}
}
class MyP extends Thread {
private MyProducerConsumer mpc;
public MyP(MyProducerConsumer mpc) {
this.mpc = mpc;
}
public void run() {
super.run();
for (int i = 0; i < 10; i++)
mpc.produce();
}
}
class MyC extends Thread {
private MyProducerConsumer mpc;
public MyC(MyProducerConsumer mpc) {
this.mpc = mpc;
}
public void run() {
super.run();
for (int i = 0; i < 10; i++)
mpc.consume();
}
}
class MyProducerConsumer {
ReentrantLock lock = new ReentrantLock();
Condition cond = lock.newCondition();
boolean hasItem = false;
public void produce() {
try {
lock.lock();
while (hasItem)
cond.await();
System.out.println("生产者生产了一个项目!");
hasItem = true;
cond.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
try {
lock.lock();
while (!hasItem)
cond.await();
System.out.println("消费者消费了一个项目!");
hasItem = false;
cond.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}