hooyantsing's Blog

19_三大不安全案例

字数统计: 599阅读时长: 2 min
2020/08/21

19 三大不安全案例

狂神说Java 多线程详解

案例

1.车站买票

车站买票,多个线程同时抢票导致一票多卖,甚至出现负数票。

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
package sync;

public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();

new Thread(station,"苦逼的我").start();
new Thread(station,"牛逼等你").start();
new Thread(station,"可恶的黄牛党").start();
}
}

class BuyTicket implements Runnable{
// 票
private int ticketNums = 10;
// 外部停止标志位
boolean flag = true;

@Override
public void run() {
// 买票
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private void buy() throws InterruptedException {
// 判断是否有票
if(ticketNums <= 0){
flag = false;
return;
}

// 模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);

}
}

运行结果:

25d190a4a4b71e24c0a037ce11985f7c.png

2.银行取钱

银行账户里有50,但你和你女朋友同时取钱,取出了150,并且账户里余额出现的负数。

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
package sync;

public class UnsafeBank {
public static void main(String[] args) {
// 账户
Account account = new Account(100,"结婚基金");

Drawing you = new Drawing(account,50,"你");
Drawing girdFriend = new Drawing(account,100,"女朋友");

you.start();
girdFriend.start();
}
}

/**
* 账户
*/
class Account{
// 余额
int money;
// 卡名
String name;

public Account(int money, String name) {
this.money = money;
this.name = name;
}
}

/**
* 银行:模拟取钱
*/
class Drawing extends Thread{
// 账户
Account account;
// 取了多少钱
int drawingMoney;
// 现在手里有多少钱
int nowMoney;

public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}

//取钱
@Override
public void run() {
// 判断有没有钱
if(account.money - drawingMoney < 0){
System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
return;
}

// sleep 可以放大问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

// 卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
// 你手里的钱
nowMoney = nowMoney + drawingMoney;

System.out.println(account.name + "余额为:" + account.money);
System.out.println(Thread.currentThread().getName() + "手里的钱:" + nowMoney);
}
}

运行结果:

46235ec922232a0057ddafa60f4cf538.png

ArrayList 数组列表是线程不安全的

开10000个线程向 ArrayList 数组列表加入元素,可能会出现同一个位置空间被多个线程重复占用,导致最后数组内元素个数少于10000个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package sync;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(
() -> list.add(Thread.currentThread().getName())
).start();
}
System.out.println(list.size());
}
}

运行结果:

%!(EXTRA markdown.ResourceType=, string=, string=)

CATALOG
  1. 1. 19 三大不安全案例
    1. 1.0.1. 案例
      1. 1.0.1.1. 1.车站买票
      2. 1.0.1.2. 2.银行取钱
      3. 1.0.1.3. ArrayList 数组列表是线程不安全的