并发编程进阶-多线程锁
# 多线程锁
提示
# 1. 乐观锁和悲观锁
# 悲观锁
概述:
认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
synchronized关键字和Lock的实现类都是悲观锁
特点:
- 适合
写操作
多的场景,先加锁可以保证写操作时数据正确 - 显示的锁定后再操作资源
- 狼性锁
代码实现
点击查看
public class LockDemo01 {
/**
* 悲观锁调用方式---synchronized
*
* @return void
* @author cnlxc
* @date 2023/4/5 23:22
*/
// 票数
private int number = 30;
// 操作方法:卖票
public synchronized void sync() {
// 判断:是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" : 卖出:"+(number--)+" 剩下:"+number);
}
}
/**
* 悲观锁调用方式---lock ReentrantLock 创建可重入锁
*
* @return void
* @author cnlxc
* @date 2023/4/5 23:22
*/
private final ReentrantLock lock = new ReentrantLock();
//卖票方法
public void lock() {
// 2. 上锁
lock.lock();
try {
// 判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName() + " :卖出" + (number--) + " 剩余:" + number);
}
} finally {
// 3. 解锁
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
# 乐观锁
概述
认为自己在使用数据时
不会有别的线程修改数据或资源
,所以不会添加锁。在Java中是通过使用
无锁编程
来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。
如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等
判断规则
版本号机制Version
- 在获取数据时,取出来的数据版本号为1,修改后版本号+1,只要修改了版本号都会递增;
- 如果修改前版本号为1,修改后版本要改为2,发现版本号已变为5,说明已有多个线程进行了修改,本次修改要进行重新处理。
最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
特点:
- 适合
读操作
多的场景,不加锁的特点能够使其读操作的性能大幅提升。 - 乐观锁则直接去操作同步资源,是一种无锁算法。
- 佛系锁
实现方式
- 采用Version版本号机制
- CAS(Compare-and-Swap, 即比较并替换)算法实现
代码实现
点击查看
/**
* 乐观锁
*
* @author cnlxc
* @date 2023/4/5 23:20
*/
private AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.incrementAndGet()
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
伪代码
# 2. Synchronized
阿里巴巴Java开发手册 (opens new window)
高并发时,同步调用应该去考量锁的性能损耗。
- 能用无锁数据结构,就不要用锁;
- 能锁区块,就不要锁整个方法体;
- 能用对象锁,就不要用类锁。
说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法
# 2.1 8锁问题
- 标准访问有a、b两个线程,请问先打印邮件还是短信
点击查看
public class Lock8Demo01 {
/**
* 演示8锁案例: 1. 标准访问有a、b两个线程,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 1. 标准访问有a、b两个线程,请问先打印邮件还是短信
Phone phone = new Phone();
new Thread(phone::sendEmail, "a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::sendSms, "b").start();
/*
结果:
------ sendEmail ------
------ sendSms ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendEmail() {
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
}
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
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
- sendEmail方法加入暂停3秒钟,请问先打印邮件还是短信
点击查看
public class Lock8Demo02 {
/**
* 演示8锁案例: 2. sendEmail方法加入暂停3秒钟,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 2. sendEmail方法加入暂停3秒钟,请问先打印邮件还是短信
Phone2 phone = new Phone2();
new Thread(phone::sendEmail, "a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::sendSms, "b").start();
/*
结果:
------ sendEmail ------
------ sendSms ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone2 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
}
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
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
- 添加一个
普通的hello方法
,请问先打印邮件还是hello
点击查看
public class Lock8Demo03 {
/**
* 演示8锁案例: 3. 添加一个普通的hello方法,请问先打印邮件还是hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 3. 添加一个普通的hello方法,请问先打印邮件还是hello
Phone3 phone = new Phone3();
new Thread(phone::sendEmail, "a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::hello, "b").start();
/*
结果:
------ hello ------
------ sendEmail ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone3 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
- 有两部手机,请问先打印邮件还是短信
点击查看
public class Lock8Demo04 {
/**
* 演示8锁案例: 4. 有两部手机,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 4. 有两部手机,请问先打印邮件还是短信
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(phone1::sendEmail, "a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone2::sendSms, "b").start();
/*
结果:
------ sendSms ------
------ sendEmail ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone4 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
- 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
点击查看
public class Lock8Demo05 {
/**
* 演示8锁案例: 5. 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 5. 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
Phone5 phone = new Phone5();
new Thread(()->{
phone.sendEmail();
}, "a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSms();
}, "b").start();
/*
结果:
------ sendEmail ------
------ sendSms ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone5 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
- 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
点击查看
public class Lock8Demo06 {
/**
* 演示8锁案例: 6. 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 6. 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
Phone6 phone1 = new Phone6();
Phone6 phone2 = new Phone6();
new Thread(()->{
phone1.sendEmail();
},"a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.sendSms();
}, "b").start();
/*
结果:
------ sendEmail ------
------ sendSms ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone6 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
- 有1个静态同步方法(发邮件),有1个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
点击查看
public class Lock8Demo07 {
/**
* 演示8锁案例: 7. 有1个静态同步方法(发邮件),有1个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 7. 有1个静态同步方法(发邮件),有1个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
Phone7 phone = new Phone7();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::sendSms, "b").start();
/*
结果:
------ sendSms ------
------ sendEmail ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone7 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
- 有1个静态同步方法(发邮件),有3个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
点击查看
public class Lock8Demo08 {
/**
* 演示8锁案例: 8. 有1个静态同步方法(发邮件),有3个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:10
*/
public static void main(String[] args) {
// 8. 有1个静态同步方法(发邮件),有3个普通同步方法(发短信),有1部手机,请问先打印邮件还是短信
Phone8 phone1 = new Phone8();
Phone8 phone2 = new Phone8();
new Thread(()->{
phone1.sendEmail();
},"a").start();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone2::sendSms, "b").start();
/*
结果:
------ sendSms ------
------ sendEmail ------
*/
}
}
/**
* 资源类
*
* @author cnlxc
* @date 2023/4/6 22:04
*/
class Phone8 {
/**
* 发邮件
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------ sendEmail ------");
}
/**
* 发短信
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:05
*/
public synchronized void sendSms() {
System.out.println("------ sendSms ------");
}
/**
* hello
*
* @return void
* @author cnlxc
* @date 2023/4/6 22:21
*/
public void hello() {
System.out.println("------ hello ------");
}
}
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
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
上次更新: 2023/04/06, 23:01:58