22Java多线程之线程安全场景举例

卖票

取钱

synchronized

继承Thread类

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
public class Test {
public static void main(String[] args) {
Account account = new Account("260731000", 100L);

Draw yourself = new Draw(account, 50L, "Yourself");
Draw girlFriend = new Draw(account, 31L, "GirlFriend");

yourself.start();
girlFriend.start();
}
}

@Data
class Account {
private String name;
private long money;

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

class Draw extends Thread {
private Account account;

private long drawMoney; // 取款金额

public Draw(Account account, long drawMoney, String threadName) {
super(threadName);
this.account = account;
this.drawMoney = drawMoney;
}

@Override
public void run() {
// synchronized(被锁对象):锁的对象就是数据修改的对象
synchronized (account) {
if (account.getMoney() - drawMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足");
return;
}

// sleep模拟取钱过程
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.printf("%s 账户余额: %d \t 取款金额: %d \t 剩余金额: %d \t 取款人: %s \n",
account.getName(),
account.getMoney(),
drawMoney,
account.getMoney() - drawMoney,
this.getName());

account.setMoney(account.getMoney() - drawMoney);
}
}
}

实现Runnable接口

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
public class Test {
public static void main(String[] args) {
Account account = new Account("260731000", 100L);

Draw yourselfDraw = new Draw(account, 50L);
Thread yourself = new Thread(yourselfDraw, "Yourself");

Draw girlFriendDraw = new Draw(account, 31L);
Thread girlFriend = new Thread(girlFriendDraw, "GirlFriend");

yourself.start();
girlFriend.start();
}
}

@Data
class Account {
private String name;
private long money;

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

class Draw implements Runnable {
private Account account;

private long drawMoney; // 取款金额

public Draw(Account account, long drawMoney) {
this.account = account;
this.drawMoney = drawMoney;
}

@Override
public void run() {
// synchronized(被锁对象):锁的对象就是数据修改的对象
synchronized (account) {
if (account.getMoney() - drawMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足");
return;
}

// sleep模拟取钱过程
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.printf("%s 账户余额: %d \t 取款金额: %d \t 剩余金额: %d \t 取款人: %s \n",
account.getName(),
account.getMoney(),
drawMoney,
account.getMoney() - drawMoney,
Thread.currentThread().getName());

account.setMoney(account.getMoney() - drawMoney);
}
}
}

ReentrantLock

有时候继承 Thread 类会更方便。

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
public class Test {
public static void main(String[] args) {
Account account = new Account("260731000", 100L);

Draw yourselfDraw = new Draw(account, 50L);
Thread yourself = new Thread(yourselfDraw, "Yourself");

Draw girlFriendDraw = new Draw(account, 31L);
Thread girlFriend = new Thread(girlFriendDraw, "GirlFriend");

yourself.start();
girlFriend.start();
}
}

@Data
class Account {
private String name;
private long money;
private final ReentrantLock lock = new ReentrantLock(true);

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

class Draw implements Runnable {
private Account account;

private long drawMoney; // 取款金额

public Draw(Account account, long drawMoney) {
this.account = account;
this.drawMoney = drawMoney;
}

@Override
public void run() {
// synchronized(被锁对象):锁的对象就是数据修改的对象
try {
account.getLock().lockInterruptibly();
if (account.getMoney() - drawMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足");
return;
}

// sleep模拟取钱过程
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.printf("%s 账户余额: %d \t 取款金额: %d \t 剩余金额: %d \t 取款人: %s \n",
account.getName(),
account.getMoney(),
drawMoney,
account.getMoney() - drawMoney,
Thread.currentThread().getName());

account.setMoney(account.getMoney() - drawMoney);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
account.getLock().unlock();
account.getLock().unlock(); // 抛出: IllegalMonitorStateException – 如果当前线程没有持有这个锁
}
}
}