多线程生产者消费者

多线程生产者消费者
生产者消费者模型

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
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
// Demo1.java
// 仓库
class Depot {
private int capacity; // 仓库的容量
private int size; // 仓库的实际数量

public Depot(int capacity) {
this.capacity = capacity;
this.size = 0;
}

public synchronized void produce(int val) {
try {
// left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
int left = val;
while (left > 0) {
// 库存已满时,等待“消费者”消费产品。
while (size >= capacity)
wait();
// 获取“实际生产的数量”(即库存中新增的数量)
// 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
// 否则“实际增量”=“想要生产的数量”
int inc = (size+left)>capacity ? (capacity-size) : left;
size += inc;
left -= inc;
System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n",
Thread.currentThread().getName(), val, left, inc, size);
// 通知“消费者”可以消费了。
notifyAll();
}
} catch (InterruptedException e) {
}
}

public synchronized void consume(int val) {
try {
// left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
int left = val;
while (left > 0) {
// 库存为0时,等待“生产者”生产产品。
while (size <= 0)
wait();
// 获取“实际消费的数量”(即库存中实际减少的数量)
// 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
// 否则,“实际消费量”=“客户要消费的数量”。
int dec = (size<left) ? size : left;
size -= dec;
left -= dec;
System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n",
Thread.currentThread().getName(), val, left, dec, size);
notifyAll();
}
} catch (InterruptedException e) {
}
}

public String toString() {
return "capacity:"+capacity+", actual size:"+size;
}
}

// 生产者
class Producer {
private Depot depot;

public Producer(Depot depot) {
this.depot = depot;
}

// 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
}

// 消费者
class Customer {
private Depot depot;

public Customer(Depot depot) {
this.depot = depot;
}

// 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
}

public class Demo1 {
public static void main(String[] args) {
Depot mDepot = new Depot(100);
Producer mPro = new Producer(mDepot);
Customer mCus = new Customer(mDepot);

mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}

某一个输出结果

1
2
3
4
5
6
7
8
9
Thread-0 produce( 60) --> left=  0, inc= 60, size= 60
Thread-4 produce(110) --> left= 70, inc= 40, size=100
Thread-3 consume(150) <-- left= 50, dec=100, size= 0
Thread-1 produce(120) --> left= 20, inc=100, size=100
Thread-2 consume( 90) <-- left= 0, dec= 90, size= 10
Thread-3 consume(150) <-- left= 40, dec= 10, size= 0
Thread-4 produce(110) --> left= 0, inc= 70, size= 70
Thread-3 consume(150) <-- left= 0, dec= 40, size= 30
Thread-1 produce(120) --> left= 0, inc= 20, size= 50

某个生产任务可能要中断,消费后再继续生产,因为仓库已经满了,某次消费也可能消费到仓库空了,必须再生产才能继续消费。这种情况,当执行notifyAll,唤醒后,自己进入等待状态。

输出结果可能会不同,因为每个线程获取锁的概率不一定,正如下面例子

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
import java.util.concurrent.TimeUnit;

public class Test {

public static void main(String[] args) throws Exception{
ShareObj obj = new ShareObj(5);
new MyThread(obj).start();
for(int i=0; i<3; i++) {
TimeUnit.SECONDS.sleep(1);
new MyThread(obj).start();
}
}
}

class ShareObj {
private int sec;

public ShareObj(int sec) {
super();
this.sec = sec;
}

public synchronized void doSomething() throws Exception {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(sec);
sec = 0;
}
}

class MyThread extends Thread {

private ShareObj obj;

public MyThread(ShareObj obj) {
super();
this.obj = obj;
}

@Override
public void run() {
try {
obj.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
}
}

问题描述:多线程处于就绪状态,当对象锁被释放时,多个线程哪个会先得到对象锁?

问题描述参考

多线程就绪时获得锁的顺序

每个新请求锁的线程先加入ContentionList队列,该队列后进先出,可以并发访问。

当Owner线程释放锁的时候,如果发现EntryList为空,则把ContentionList的等待队列放入EntryList,并把EntryList的head指定为OnDeck线程,这就很好的解释了,当上面描述问题中,如果创建线程 时候如果执行了

1
TimeUnit.SECONDS.sleep(1);

那么ContentionList必然是一个倒叙输出,就是最后创建的线程先得到锁,我指的是当第一个线程睡眠五秒,然后每秒创建一个线程,共创建三个线程,输出的结果一直都是

1
2
3
4
Thread-0
Thread-3
Thread-2
Thread-1

如果你设置睡眠五秒,创建8个线程,每次创建休眠1秒则会得到这个结果

1
2
3
4
5
6
7
8
9
Thread-0
Thread-4
Thread-3
Thread-2
Thread-1
Thread-5
Thread-6
Thread-7
Thread-8

在睡眠五秒内,4 3 2 1被加入到ContionList而后Thread-0线程释放锁后又被移入EntryList,并4线程处于OnDeck状态。后续获得锁的线程必然是 4 3 2 1这个顺序直到EntryList为空。当EntryList的线程的最后一个即1执行完时,ContentList只有一个线程5生成,被移动到EntryList,后续的操作都是恰好保持ContionList和EntryList始终只有一个线程。后续都会顺序输出,不管你创建8个还是20个线程。

如果在创建线程的时候不执行

1
TimeUnit.SECONDS.sleep(1);

则,进入ContionList的顺讯随机,因为ContionList可以并发访问,导致顺序不能确定,