线程同步学习笔记(生产者消费者)

本文详细介绍了生产者消费者模型的实现,包括手动实现面包仓库类SyncStack,多线程间的同步机制Thread.sleep()和wait()方法的应用,以及wait()方法在while循环中的使用原因。通过实例展示了生产者线程与消费者线程之间的交互,以及在面包仓库满时的同步处理过程。
package Thread;

/**
 * 生产者消费者过程模拟
 * 
 * @author ZHI
 * 
 */
public class ProducerConsumer {

	public static void main(String[] args) {
		SyncStack ss = new SyncStack();
		Producer p = new Producer(ss);
		Consumer c = new Consumer(ss);
		new Thread(p).start();
		new Thread(c).start();
	}
}

// 面包类
class Bread {
	private int id;

	public Bread(int id) {
		this.id = id;
	}
}

// 面包仓库类
class SyncStack {
	Bread[] bArr = new Bread[10];
	private int index = 0;

	public int getIndex() {
		return index;
	}

	public synchronized void push(Bread b) {
		while (index == bArr.length) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		this.notifyAll();
		bArr[index] = b;
		System.out.println("生产者生产了:" + index);
		index++;
	}

	public synchronized Bread pop() {
		while (index == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		this.notifyAll();
		index--;
		System.out.println("消费者消费了:" + index);
		return bArr[index];
	}
}

// 生产者类
class Producer implements Runnable {
	SyncStack ss;

	public Producer(SyncStack ss) {
		this.ss = ss;
	}

	public void run() {
		for (int i = 0; i < 30; i++) {
			Bread b = new Bread(i);
			ss.push(b);

			try {
				Thread.sleep(100);//每生产一个面包,休眠100
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

// 消费者类
class Consumer implements Runnable {
	SyncStack ss;

	public Consumer(SyncStack ss) {
		this.ss = ss;
	}

	public void run() {
		for (int i = 0; i < 30; i++) {
			ss.pop();

			try {
				Thread.sleep(500);//每消费一个面包,休眠500
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}



以上是一个典型的生产者消费者的例子。其实编程过程中本人的主要问题有以下几点:

① Stack(堆栈)的手动实现

见上述待遇面包仓库类SyncStack的实现,一般的Stack代码实现如下:

class Stack {
	int[] intArr = new int[10];// Stack的容量
	private int index = 0;// 元素索引

	public void push(int i) {// 入栈方法
		intArr[index] = i;
		index++;
	}

	public int pop() {// 出栈方法
		index--;
		return intArr[index];
	}
}

② Thread.sleep()方法的功能

Thread.sleep()方法表示让当前正在执行的线程休眠,但不会释放对象锁,任何代码中出现Thread.sleep()方法即表示当前执行该代码的线程进入休眠状态。注意与wait()方法的区别。

③ 为什么wait()方法要放在while循环里

如果wait()放在if中,即

	public synchronized Bread pop() {
		if (index == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		this.notifyAll();
		index--;
		System.out.println("消费者消费了:" + index);
		return bArr[index];
	}

当程序执行wait时,线程突然被打断,即跳到catch中,随后跳出while循环,然后继续执行接下来的代码,但显然这不是我们需期望的。我们希望程序在执行wait被打断时,仍然能够再次回去执行wait。所以只能把wait方法放在while循环之中。

④ wait()和notify()的用法,及其在整个代码执行过程中的流程功能。

wait()和notify()两个方法只能用在一个对象的同步方法中或取得对象锁的同步代码块中,而且对象调用wait()方法后当前锁定该对象的线程会释放锁并进入等待状态,这个等待的线程只有等到该对象再次调用notify()方法时,才会再次进入竞争对象锁的就绪状态。而对象调用notify()方法时,当前锁定该对象的线程不会释放锁,即表示该线程会继续执行。

上述代码的执行结果如下:

生产者生产了:0
消费者消费了:0
生产者生产了:0
生产者生产了:1
生产者生产了:2
生产者生产了:3
消费者消费了:3
生产者生产了:3
生产者生产了:4
生产者生产了:5
生产者生产了:6
生产者生产了:7
消费者消费了:7
生产者生产了:7
生产者生产了:8
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
生产者生产了:9
消费者消费了:9
消费者消费了:8
消费者消费了:7
消费者消费了:6
消费者消费了:5
消费者消费了:4
消费者消费了:3
消费者消费了:2
消费者消费了:1
消费者消费了:0

当main方法里两个线程(Thread(p)和Thread(c))同时启动时,即生产者线程和消费者线程同时竞争同一个面包仓库类SyncStack。

而开始时仓库里没有元素,若消费者线程首先拿到仓库类的对象锁,即执行pop()时,消费者线程就会进入等待状态,并释放对象锁,此时生产者线程即可取得仓库对象锁并执行push()方法,而执行push方法时,同时会执行notify()方法,此时之前进入等待锁的消费者线程就会进入竞争对象锁的就绪状态,但消费者线程并不会立即执行。然后生产者线程的push()方法执行完毕后,就释放对象锁。

但由于程序中生产者线程每生产一个面包后休眠的时间要比消费者线程每消费一个面包后休眠的时间短得多,故生产者线程生产面包的速度要快于消费者线程消费面包的过程,故可以看到生产者之后就会一直生产面包,直到有一个消费者线程休眠结束后消费一个面包。

当面包仓库满时,生产者线程执行push()方法时会进入等待状态,直到消费者线程执行pop()方法消费一个面包,并执行notify方法,从而通知生产者线程进入竞争锁的就绪状态,生产者线程取得锁之后立马生产一个面包后随即再次执行生产面包操作时(由于生产速度远快于消费速度),便会再次进入等待状态,如此循环,在仓库满后,每消费一个面包后立马生产一个面包。

其实从上述代码中也可以看出,SyncStack仓库类中的两个同步方法push()和pop()中各自的判断条件决定了最初的执行步骤绝对是生产者先生产第一个面包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值