文章目录
一 、实例引出
在线程操作中有一个经典案例程序,即生产者和消费者,生产者不断生产,消费者不断取走生产者生产的产品
二 、程序的基本实现
观察以下程序,会产生什么问题:
1 、定义一个保存信息的类 Info.java
//产品类
class Info {
private String name;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
//生产者,循环生成产品
class Product implements Runnable{
private Info info = null;
public Product(Info info){
this.info = info;
}
public void run(){
Boolean flag = false;
for (int i=0; i<30; i++){
if (flag){
this.info.setName("A产品");
try {
Thread.sleep(90); //加入延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setContent("对应A产品的");
flag = false;
}else{
this.info.setName("B产品");
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setContent("对应B产品的");
flag = true;
}
}
}
}
//消费者,循环取走产品
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
public void run(){
for (int i=0;i<30;i++){
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.info.getName() + "---->" + this.info.getContent());
}
}
}
//测试类
public class ExampleDemo {
public static void main(String[] args) {
Info i = new Info();
Product pro = new Product(i);
Consumer con = new Consumer(i);
new Thread(pro).start();
new Thread(con).start();
}
}
程序运行结果(部分截图):
观察运行结果,结合线程运行的不确定性,可以发现以上程序存在两个问题:
1、数据紊乱 2、没有实现生产一个,取走一个
原因在于:
(1)、生产者只设置name,还未设置content 时,程序就切换到消费者线程,造成数据紊乱
(2)、生产者设置多个产品数据后,消费者才开始取,或者消费者取走一个数据后,生产者还未生产新的数据,消费者重复取已经取过的数据,所以没有实现生产一个,取走一个
三 、解决问题1 ------- 加入同步,保证数据一致
//产品类
class Info {
private String name;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public synchronized void set(String name,String content){
this.setName(name);
try {
Thread.sleep(90); //加入延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
}
public synchronized void get(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "---->" + this.getContent());
}
}
//生产者,循环生成产品
class Product implements Runnable{
private Info info = null;
public Product(Info info){
this.info = info;
}
public void run(){
Boolean flag = false;
for (int i=0; i<30; i++){
if (flag){
this.info.set("A产品","对应A产品的");
flag = false;
}else{
this.info.set("B产品","对应B产品的");
flag = true;
}
}
}
}
//消费者,循环取走产品
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
public void run(){
for (int i=0;i<30;i++){
this.info.get();
}
}
}
//测试类
public class ExampleDemo {
public static void main(String[] args) {
Info i = new Info();
Product pro = new Product(i);
Consumer con = new Consumer(i);
new Thread(pro).start();
new Thread(con).start();
}
}
程序运行结果(部分截图):
加入同步,解决了问题1,数据错乱问题,还存在问题2,重复取值,没有实现生产一个,取走一个
四 、Object类对线程的支持 ------ 等待与唤醒
五 、解决问题2 ------ 加入等待与唤醒
生产者生产过程中,不能取走,消费者线程处于 等待 状态,生产完成,唤醒消费者线程可以取走产品,生产者进入 等待 状态,不能生产。等 消费者 取走产品后,生产者才可被唤醒继续生产
//产品类
class Info {
private String name;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
//设置标志,判断什么时候进入等待状态,什么时候唤醒
//false为等待,不可以生产,可以取走
// true为唤醒,继续生产,不可取走
private boolean flag = true; //可生产
public synchronized void set(String name,String content){
if(!flag){ //如果falg为false
try {
super.wait(); //通过super关键字调用父类Object方法,线程进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(90); //加入延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
flag = false; //不可生产,可取走
super.notify(); //通过super关键字调用父类Object方法,唤醒消费者线程
}
public synchronized void get(){
if(flag){
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "---->" + this.getContent());
flag = true;
super.notify(); //唤醒生产者
}
}
//生产者,循环生成产品
class Product implements Runnable{
private Info info = null;
public Product(Info info){
this.info = info;
}
public void run(){
Boolean flag = true;
for (int i=0; i<30; i++){
if (flag){
this.info.set("A产品","对应A产品的");
flag = false;
}else{
this.info.set("B产品","对应B产品的");
flag = true;
}
}
}
}
//消费者,循环取走产品
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
public void run(){
for (int i=0;i<30;i++){
this.info.get();
}
}
}
//测试类
public class ExampleDemo02 {
public static void main(String[] args) {
Info i = new Info();
Product pro = new Product(i);
Consumer con = new Consumer(i);
new Thread(pro).start();
new Thread(con).start();
}
}
程序运行结果(部分截图):
生产者每生产一个,就要等待消费者取走。
消费者每取走一个,就要等待生产者生产,
这样就避免了重复生产和重复取走的问题