Java多线程同步Demo

学习Java多线程的过程中,为了练习掌握的知识,花了几个小时写了一个小Demo,业务场景为:10个同学,每人的任务是看2本书,书架上只有7本书,于是后面的同学需要等前面同学看完才能看,通过同步代码块实现了这样一个业务。
不足之处希望大牛指出。

结果展示

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
0号同学借了2本书,书架上还有5本书
1号同学借了2本书,书架上还有3本书
2号同学借了2本书,书架上还有1本书
0号同学读完之后,把书还了回去,书架上还有3本书
0号同学读书任务完成
9号同学借了2本书,书架上还有1本书
1号同学读完之后,把书还了回去,书架上还有3本书
1号同学读书任务完成
3号同学借了2本书,书架上还有1本书
2号同学读完之后,把书还了回去,书架上还有3本书
2号同学读书任务完成
8号同学借了2本书,书架上还有1本书
9号同学读完之后,把书还了回去,书架上还有3本书
9号同学读书任务完成
4号同学借了2本书,书架上还有1本书
3号同学读完之后,把书还了回去,书架上还有3本书
3号同学读书任务完成
7号同学借了2本书,书架上还有1本书
8号同学读完之后,把书还了回去,书架上还有3本书
8号同学读书任务完成
5号同学借了2本书,书架上还有1本书
4号同学读完之后,把书还了回去,书架上还有3本书
4号同学读书任务完成
6号同学借了2本书,书架上还有1本书
7号同学读完之后,把书还了回去,书架上还有3本书
7号同学读书任务完成
5号同学读完之后,把书还了回去,书架上还有5本书
5号同学读书任务完成
6号同学读完之后,把书还了回去,书架上还有7本书
6号同学读书任务完成

书架对象

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
public class Bookshelf {
//书架上书籍总数
public int shelfBooks;
//创建一个锁对象
private final Object lockObj = new Object();
//构造方法
public Bookshelf(int books) {
this.shelfBooks = books;
}
//借书的方法
public int borrow(Person person, int num) {
//同步代码块
synchronized(lockObj){
//如果书架上书不够,就不能借出了
while (shelfBooks < num){
try {
//这时则需要等待别人来还书
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//书架上的书够了,才可以借走
person.borrow(num);
this.shelfBooks -= num;
//释放锁
lockObj.notifyAll();
//用return而不是get,为了保证数据的时效性
return this.shelfBooks;
}
}
//还书的方法
public int back(Person person, int num) {
//同步代码块
synchronized(lockObj) {
//这里没有什么必要条件,直接运行即可
person.back(num);
this.shelfBooks += num;
//释放锁
lockObj.notifyAll();
//用return而不是get,为了保证数据的时效性
return this.shelfBooks;
}
}
}

同学对象

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
public class Person {
//同学名字
private String name;
//每个同学手里的书本量
private Integer books;
public Integer getBooks() {
return books;
}
public void setBooks(Integer books) {
this.books = books;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, Integer books) {
this.name = name;
this.books = books;
}
//可以不设置这个方法,用set即可
public void borrow(int num) {
this.books += num;
}
//可以不设置这个方法,用set即可
public void back(int num){
if (this.books >= num) this.books -= num;
}
}

读书任务

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
public class ReadBookTask implements Runnable {
//书架
private Bookshelf bookshelf;
//同学
private Person person;
//任务量(每次借几本书)
private int num;
//休眠时间
private int DELAY = 1000;
@Override
public void run() {
try{
while (true){
int borrow = bookshelf.borrow(person, num);
System.out.println(person.getName() + "借了" + person.getBooks() + "本书,书架上还有" + borrow + "本书");
Thread.sleep(DELAY);
int back = bookshelf.back(person, num);
System.out.println(person.getName() + "读完之后,把书还了回去,书架上还有" + back + "本书");
System.out.println(person.getName() + "读书任务完成");
//读书完成之后可以跳出循环
//不跳出也可以,书的总量不会改变
return;
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
//构造方法
public ReadBookTask(Bookshelf bookshelf, Person person, int num, int DELAY) {
this.bookshelf = bookshelf;
this.person = person;
this.num = num;
this.DELAY = DELAY;
}
}

测试方法

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
public class ReadTest {
public static void main(String[] args){
//手动设置属性比较繁琐
ArrayList<Person> list = new ArrayList<>();
//设置i为一共有10个同学
for (int i = 0; i < 10; i++) {
list.add(new Person(i + "号同学", 0));
}
//设置书架上一共有7书
Bookshelf bookshelf = new Bookshelf(7);
//此处为遍历,即开始的时候线程还是有顺序的,拿到锁的线程释放锁之后,其它线程才会开始抢锁
for (Person person : list) {
//设置num为每个人的读书量为2
ReadBookTask task = new ReadBookTask(bookshelf, person, 2, 1000);
//给每条线程附上对应的同学名字
Thread t = new Thread(task, person.getName());
//开启线程
t.start();
try {
//设置sleep时间,排队取书,不要插队(排在前面的同学先把书借走了,后面的学生开始抢)
Thread.sleep(100);
} catch (InterruptedException e) {
//InterruptedException异常是对阻塞方法(比如sleep)的一个良好的提示异常
e.printStackTrace();
}
}
}
}

感谢看到这里,不足之处希望指出。