在多线程的程序中,除了要放置资源冲突外,还要保证线程的同步。
通过生产者消费者模型来验证线程的同步和资源共享问题。
假设有一个生产者Producer,一个消费者Consumer
生产者产生0~9的整数,将他们存储在Box中。要求生产者生产一个数字,消费者取得一个数字,这就涉及到了线程的同步的问题。
这个问题可以通过两个线程实现生产者和消费者,它们共享Box对象。如果不加控制就得不到预期的效果。
1. 不同步的设计
//需要被共享的Box对象
public class Box {
private int data;
public synchronized void put(int value){
data = value;
}
public synchronized int get(){
return data;
}
}
//测试类
public class ProducerConsumerTest {
public static void main(String[] args) {
Box box = new Box();
Producer producer = new Producer(box);
Consumer consumer = new Consumer(box);
producer.start();
consumer.start();
}
static class Producer extends Thread {
// 被共享的对象
private Box box;
public Producer(Box c) {
box = c;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
box.put(i);
System.out.println("Producer put: " + i);
try {
sleep((int) Math.random() * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer extends Thread {
private Box box;
public Consumer(Box c) {
box = c;
}
@Override
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = box.get();
System.out.println("Consumer get: " + value);
}
}
}
}
某次执行结果:
Producer put: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Consumer get: 0
Producer put: 1
Producer put: 2
Producer put: 3
Producer put: 4
Producer put: 5
Producer put: 6
Producer put: 7
Producer put: 8
Producer put: 9
从执行结果可以看出,尽管使用了synchronized关键字实现了对象锁,单这还不够。
如果生产者的速度比消费者速度快,那么在消费者还没来得及取出前一个数据的情况下,生产者又产生了新数据,于是消费者就会跳过前一个数据;
反之,如果消费者的速度比生产者的速度快,那么在生产者还没生产下一个数据前,消费者可能两次取出同一个数据,也就是类似本次执行结果这样
下面我们利用监视器模型来修改一下
2. 监视器模型
为了避免上述情况的发生,就必须使生产者线程想Box对象中存入数据与消费者线程从Box对象中去的数据同步。
为了达到这一目的,我们可以采用监视器模型,通过调用wait和notify方法实现同步。
修改代码如下:
public class MonitorBox {
private int data;
// 用来表示数据是否可用
private boolean available = false;
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data = value;
available = true;
notifyAll();
}
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll();
return data;
}
}
public class ProducerConsumerTest {
public static void main(String[] args) {
MonitorBox box = new MonitorBox();
Producer producer = new Producer(box);
Consumer consumer = new Consumer(box);
producer.start();
consumer.start();
}
static class Producer extends Thread {
// 被共享的对象
private MonitorBox box;
public Producer(MonitorBox c) {
box = c;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
box.put(i);
System.out.println("Producer put: " + i);
try {
sleep((int) Math.random() * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer extends Thread {
private MonitorBox box;
public Consumer(MonitorBox c) {
box = c;
}
@Override
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = box.get();
System.out.println("Consumer get: " + value);
}
}
}
}
某次执行结果
Producer put: 0
Consumer get: 0
Producer put: 1
Producer put: 2
Consumer get: 1
Consumer get: 2
Producer put: 3
Consumer get: 3
Producer put: 4
Consumer get: 4
Producer put: 5
Consumer get: 5
Producer put: 6
Consumer get: 6
Producer put: 7
Producer put: 8
Consumer get: 7
Consumer get: 8
Consumer get: 9
Producer put: 9
从上面结果可以看出,虽然仍存在消费者和生产者线程快慢的问题,但是生产者和消费者线程基本实现了同步。