并发编程基础-多线程锁
# 多线程锁
# 1. Synchronized锁的八种情况
synchronized 实现同步的基础:Java 中的每一个对象都可以作为锁。
具体表现为以下 3 种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的 Class 对象。
对于同步方法块,锁是 Synchonized 括号里配置的对象
# 2. 公平锁和非公平锁 😄
API
使用卖票案例来展示两种锁的情况。
# 非公平锁
使用ReentrantLock
的无参构造、或者有参构造时,参数为: false
// 情况一
new ReentrantLock();
// 情况二
new ReentrantLock(false);
2
3
4
代码实现
**
* 第一步 创建资源类,定义属性和和操作方法
*/
class LTicket {
//票数量
private int number = 30;
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
//卖票方法
public void sale() {
//上锁
lock.lock();
try {
//判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName() + " :卖出" + (number--) + " 剩余:" + number);
}
} finally {
//解锁
lock.unlock();
}
}
}
/**
* lock 使用
*/
public class LSaleTicket {
/**
* 第二步 创建多个线程,调用资源类的操作方法 创建三个线程
* @param args
*/
public static void main(String[] args) {
LTicket ticket = new LTicket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "AA").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "BB").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "CC").start();
}
}
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
结果:
点击查看
# 结果情况1: 有可能都卖给1
AA :卖出30 剩余:29
AA :卖出29 剩余:28
BB :卖出28 剩余:27
BB :卖出27 剩余:26
BB :卖出26 剩余:25
BB :卖出25 剩余:24
BB :卖出24 剩余:23
BB :卖出23 剩余:22
BB :卖出22 剩余:21
BB :卖出21 剩余:20
BB :卖出20 剩余:19
BB :卖出19 剩余:18
BB :卖出18 剩余:17
BB :卖出17 剩余:16
BB :卖出16 剩余:15
BB :卖出15 剩余:14
AA :卖出14 剩余:13
AA :卖出13 剩余:12
AA :卖出12 剩余:11
AA :卖出11 剩余:10
AA :卖出10 剩余:9
AA :卖出9 剩余:8
AA :卖出8 剩余:7
AA :卖出7 剩余:6
AA :卖出6 剩余:5
AA :卖出5 剩余:4
AA :卖出4 剩余:3
AA :卖出3 剩余:2
AA :卖出2 剩余:1
AA :卖出1 剩余:0
# 结果情况2: AA、CC 都卖出去一部分,BB没卖出去
AA :卖出30 剩余:29
CC :卖出29 剩余:28
CC :卖出28 剩余:27
CC :卖出27 剩余:26
CC :卖出26 剩余:25
CC :卖出25 剩余:24
CC :卖出24 剩余:23
CC :卖出23 剩余:22
CC :卖出22 剩余:21
CC :卖出21 剩余:20
CC :卖出20 剩余:19
CC :卖出19 剩余:18
CC :卖出18 剩余:17
CC :卖出17 剩余:16
CC :卖出16 剩余:15
CC :卖出15 剩余:14
CC :卖出14 剩余:13
CC :卖出13 剩余:12
CC :卖出12 剩余:11
CC :卖出11 剩余:10
CC :卖出10 剩余:9
CC :卖出9 剩余:8
CC :卖出8 剩余:7
CC :卖出7 剩余:6
CC :卖出6 剩余:5
CC :卖出5 剩余:4
CC :卖出4 剩余:3
CC :卖出3 剩余:2
CC :卖出2 剩余:1
CC :卖出1 剩余:0
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
# 公平锁
或者有参构造时,参数为: true
new ReentrantLock(true);
代码实现:
class LTicket {
//票数量
private int number = 30;
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
//卖票方法
public void sale() {
//上锁
lock.lock();
try {
//判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName() + " :卖出" + (number--) + " 剩余:" + number);
}
} finally {
//解锁
lock.unlock();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 两种锁特点
非公平锁:线程之间抢着执行,可能出现一个线程不停工作,另一个线程一直停歇的状态,但该方式的执行效率高
。
公平锁:线程之间公平执行,但执行效率相对于非公平锁低
。
# 3. 可重入锁 😄
提示
syschronized(隐式)
和lock(显示)
都是可重入锁。
# 3.1 概述
通过设置的同一把锁
可以自由进出第一层、第二层、第三层。即为可重入锁
(递归锁)。
比如:回家开锁后,可以自由进出卧室,书房、卫生间等。
# 3.2 Syschronized 代码实现
Syschronized :隐式可重入锁,自动上锁,不用手动添加锁。
public class SyncLockDemo {
public synchronized void add() {
add();
}
public static void main(String[] args) {
// synchronized 演示可重入锁
// synchronized同步代码块
Object o = new Object();
new Thread(()->{
synchronized(o) {
System.out.println(Thread.currentThread().getName()+" 外层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 中层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 内层");
}
}
}
},"t1").start();
// synchronized同步方法
new SyncLockDemo().add();
}
}
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
结果:
t1 外层
t1 中层
t1 内层
Exception in thread "main" java.lang.StackOverflowError
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
at cn.cnlxc.sync.SyncLockDemo.add(SyncLockDemo.java:11)
2
3
4
5
6
7
8
9
10
11
12
13
14
可重入锁又叫递归锁,出现下面情况说明
add
方法被执行了,证明synchronized
为可重入锁
# 3.3 Lock 代码实现
Lock:显示可重入锁。需要手动上锁,释放锁。
public class SyncLockDemo {
public static void main(String[] args) {
//Lock演示可重入锁
Lock lock = new ReentrantLock();
//创建线程
new Thread(() -> {
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 外层");
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 内层");
} finally {
//释放锁
lock.unlock();
}
} finally {
//释放做
lock.unlock();
}
}, "t1").start();
//创建新线程
new Thread(() -> {
lock.lock();
System.out.println("aaaa");
lock.unlock();
}, "aa").start();
}
}
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
情况一:所有上的锁正常释放 线程 t1,aa正常执行
t1 外层
t1 内层
aaaa
2
3
情况2:线程t1,没有释放锁。此时程序不会结束,线程aa不会执行
t1 外层
t1 内层
2
3
注意:所有创建的锁都应该上锁、释放锁。
# 4. 死锁
# 4.1 概述
死锁
: 两个或两个以上的进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干扰,它们就无法在执行下去。
# 4.2 产生死锁的原因
- 系统资源不足
- 进程运行推进的顺序不合适
- 资源分配不当
# 4.3 示例
/**
* 演示死锁
*/
public class DeadLock {
//创建两个对象
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 持有锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 获取锁b");
}
}
},"A").start();
new Thread(()->{
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 持有锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 获取锁a");
}
}
},"B").start();
}
}
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
# 4.4 验证是否为死锁
怎样证明写的程序是死锁,而不是for循环、微服务之间的调用造成的原因。
步骤一:jps
jps # 类似Linux ps -ef
使用此命令需要确保已配置号JAVA环境变量,在命令号使用上述命令
示例
24240
23432 DeadLock
24392 Launcher
17516 Jps
2
3
4
步骤二:jstack
jstack:jvm自带堆栈跟踪工具
jstack PID # pid 需要查看程序编号
示例
jstack 23432
结果:
点击查看
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.192-b12 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000003022800 nid=0x69fc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"B" #13 prio=5 os_prio=0 tid=0x00000000290c6800 nid=0x954 waiting for monitor entry [0x0000000029a1e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at cn.cnlxc.sync.DeadLock.lambda$main$1(DeadLock.java:38)
- waiting to lock <0x0000000719f4cac0> (a java.lang.Object)
- locked <0x0000000719f4cad0> (a java.lang.Object)
at cn.cnlxc.sync.DeadLock$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"A" #12 prio=5 os_prio=0 tid=0x000000002902e800 nid=0x6e6c waiting for monitor entry [0x000000002991e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at cn.cnlxc.sync.DeadLock.lambda$main$0(DeadLock.java:24)
- waiting to lock <0x0000000719f4cad0> (a java.lang.Object)
- locked <0x0000000719f4cac0> (a java.lang.Object)
at cn.cnlxc.sync.DeadLock$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x00000000274bc000 nid=0x72f8 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x0000000027406800 nid=0x6e10 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000027405000 nid=0x5358 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000027404000 nid=0x5654 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x00000000273f9000 nid=0x25d0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x00000000273fc800 nid=0x6b1c runnable [0x0000000028a1e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000071a0c2ba0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000071a0c2ba0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:56)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000002735c000 nid=0x27b0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000273b5800 nid=0x5f74 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000311c000 nid=0x6620 in Object.wait() [0x000000002869e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000719e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x0000000719e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000025c4d800 nid=0x5fdc in Object.wait() [0x000000002859f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000719e06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0000000719e06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x0000000025c46800 nid=0x198c runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000003038000 nid=0x6c1c runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000303a000 nid=0x7250 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000303b800 nid=0x621c runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000303d000 nid=0x719c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000003040000 nid=0x6ddc runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000003041800 nid=0x70a8 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000003044800 nid=0x7350 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000003045800 nid=0x7044 runnable
"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000000003047000 nid=0x6b24 runnable
"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000000003048000 nid=0x2fd0 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x00000000274ea000 nid=0x720c waiting on condition
JNI global references: 317
Found one Java-level deadlock:
=============================
"B":
waiting to lock monitor 0x0000000025c50d78 (object 0x0000000719f4cac0, a java.lang.Object),
which is held by "A"
"A":
waiting to lock monitor 0x0000000025c533f8 (object 0x0000000719f4cad0, a java.lang.Object),
which is held by "B"
Java stack information for the threads listed above:
===================================================
"B":
at cn.cnlxc.sync.DeadLock.lambda$main$1(DeadLock.java:38)
- waiting to lock <0x0000000719f4cac0> (a java.lang.Object)
- locked <0x0000000719f4cad0> (a java.lang.Object)
at cn.cnlxc.sync.DeadLock$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"A":
at cn.cnlxc.sync.DeadLock.lambda$main$0(DeadLock.java:24)
- waiting to lock <0x0000000719f4cad0> (a java.lang.Object)
- locked <0x0000000719f4cac0> (a java.lang.Object)
at cn.cnlxc.sync.DeadLock$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
从上出结果看出该程序产生了死锁。
# 5. 悲观锁和乐观锁
# 悲观锁
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
Java 中synchronized
和ReentrantLock
等独占锁就是悲观锁思想的实现。
悲观锁通常多用于写多比较多的情况下(多写场景),避免频繁失败和重试影响性能。
# 乐观锁
乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。
在 Java 中java.util.concurrent.atomic
包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。
乐观锁通常多于写比较少的情况下(多读场景),避免频繁加锁影响性能,大大提升了系统的吞吐量。
# 6. 表锁和行锁
行锁容易产生死锁
# 7. 读写锁 (共享锁和排它锁)
# 7.1 读写锁概述
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
- 针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为
共享锁
;一个是写相关的锁,称为排它锁
(独占锁)。两个锁都容易产生死锁。
读写锁: 一个资源可以被多个线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读读共享,读写互斥,写写互斥。
# 7.2 ReentrantReadWriteLock 应用
点击查看
class MyCache {
// 创建map集合
private volatile Map<String, Object> map = new HashMap<>();
// 放数据
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "正在写操作" + key);
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 放数据
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写完了" + key);
}
// 取数据
public Object get(String key) {
Object result = null;
System.out.println(Thread.currentThread().getName() + "正在读取操作" + key);
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 放数据
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完了" + key);
return result;
}
}
class readWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 创建线程放数据
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.put(num+ "", num + "");
}, String.valueOf(i)).start();
}
// 创建线程取数据
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num+ "");
}, String.valueOf(i)).start();
}
}
}
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
结果
1正在写操作1
2正在写操作2
4正在写操作4
5正在写操作5
3正在写操作3
1正在读取操作1
2正在读取操作2
4正在读取操作4
3正在读取操作3
5正在读取操作5
2读取完了2
1写完了1
1读取完了1
5读取完了5
5写完了5
3写完了3
3读取完了3
4写完了4
4读取完了4
2写完了2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
问题:只有写完了才能读取数据。
加读取锁改造
class MyCache {
// 创建map集合
private volatile Map<String, Object> map = new HashMap<>();
// 创建读写锁
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 放数据
public void put(String key, Object value) {
// 添加写锁
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在写操作" + key);
// 暂停一会
TimeUnit.MICROSECONDS.sleep(300);
// 放数据
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写完了" + key);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
// 释放写锁
rwLock.writeLock().unlock();
}
}
// 取数据
public Object get(String key) {
Object result = null;
// 添加读锁
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在读取操作" + key);
// 暂停一会
TimeUnit.MICROSECONDS.sleep(300);
// 放数据
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完了" + key);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
// 释放读锁
rwLock.readLock().unlock();
}
return result;
}
}
class readWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 创建线程放数据
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.put(num+ "", num + "");
}, String.valueOf(i)).start();
}
// 创建线程取数据
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num+ "");
}, String.valueOf(i)).start();
}
}
}
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
结果
点击查看
1正在写操作1
1写完了1
2正在写操作2
2写完了2
3正在写操作3
3写完了3
4正在写操作4
4写完了4
5正在写操作5
5写完了5
1正在读取操作1
2正在读取操作2
3正在读取操作3
4正在读取操作4
5正在读取操作5
2读取完了2
1读取完了1
5读取完了5
4读取完了4
3读取完了3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
从结果可以看出:写锁为独占锁,读锁为共享锁。
# 7.3 读写锁的演变
# 7.4 读写锁的降级
# 概述
锁降级
:将写锁降级为读锁。
JDK8说明:
# 示例
- 进行写操作时候,可以进行读操作
点击查看
public class Demo1 {
public static void main(String[] args) {
// 可重入读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();// 读锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();// 写锁
// 1 获取写锁
writeLock.lock();
System.out.println("cnlxc.cn write");
// 2 获取读锁
readLock.lock();
System.out.println("---read");
// 3 释放写锁
writeLock.unlock();
// 4 释放读锁
readLock.unlock();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
结果:
cnlxc.cn write
---read
2
- 进行读操作后,不能进行写操作
点击查看
public class Demo1 {
public static void main(String[] args) {
// 可重入读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();// 读锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();// 写锁
// 2 获取读锁
readLock.lock();
System.out.println("---read");
// 1 获取写锁
writeLock.lock();
System.out.println("cnlxc.cn write");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
结果:
---read // 程序未执行完