关于Java多线程---------(4,经典案例-生产者与消费者、线程的生命周期)

本文介绍了一个典型的生产者消费者模式案例,通过多线程机制实现生产者不断生产信息,消费者不断消费信息的过程。针对同步问题,文章详细解释了如何通过同步方法和等待唤醒机制确保数据的一致性和完整性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程同步操作

生产者消费者案例

生产者不断生产,消费者取走生产者生产的产品;
生产者生产的是信息,就可以定义一个信息类,生产者和消费者同时占有这个信息类的引用,那么,就可以将生产者和消费者两个线程通过信息类联合在一起。
生产者实现多线程机制;消费者实现多线程机制;

class Info{	// 定义信息类
	private String name = "李华";	 // 定义name属性
	private String content = "JAVA讲师"  ;		// 定义content属性
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};
class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = false ;	// 定义标记位
		for(int i=0;i<50;i++){
			if(flag){
				this.info.setName("李华") ;	// 设置名称
				try{
					Thread.sleep(90) ;
				}catch(InterruptedException e){
					e.printStackTrace() ;
				}
				this.info.setContent("JAVA讲师") ;	// 设置内容
				flag = false ;
			}else{
				this.info.setName("mldn") ;	// 设置名称
				try{
					Thread.sleep(90) ;
				}catch(InterruptedException e){
					e.printStackTrace() ;
				}
				this.info.setContent("www.mldnjava.cn") ;	// 设置内容
				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<50;i++){
			try{
				Thread.sleep(90) ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
			System.out.println(this.info.getName() + 
				" --> " + this.info.getContent()) ;
		}
	}
};
public class ThreadCaseDemo01{
	public static void main(String args[]){
		Info info = new Info();	// 实例化Info对象
		Producer pro = new Producer(info) ;	// 生产者
		Consumer con = new Consumer(info) ;	// 消费者
		new Thread(pro).start() ;
		new Thread(con).start() ;
	}
};

生产者生产50次信息,为了更好的发现问题,中间加入了延迟;然后是消费者,消费者不停的取出;最后进行测试;
在这里插入图片描述

问题

从测试运行记过看,生产的消息有重复取,有消息错乱的情况出现,消息1的name和消息2的content被成对的取出来了;

怎么解决呢?
之所以出现信息错乱不对应的情况,是因为中间加入了延迟操作,所以产生了不同步的问题,那么就可以使用解决该问题;使用同步方法,或者同步代码块;
怎么改?

问题解决
class Info{	// 定义信息类
	private String name = "李兴华";	 // 定义name属性
	private String content = "JAVA讲师"  ;		// 定义content属性
	public synchronized void set(String name,String content){
		this.setName(name) ;	// 设置名称
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		this.setContent(content) ;	// 设置内容
	}
	public synchronized void get(){
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		System.out.println(this.getName() + 
			" --> " + this.getContent()) ;
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};
class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = false ;	// 定义标记位
		for(int i=0;i<50;i++){
			if(flag){
				this.info.set("李兴华","JAVA讲师") ;	// 设置名称
				flag = false ;
			}else{
				this.info.set("mldn","www.mldnjava.cn") ;	// 设置名称
				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<50;i++){
			this.info.get() ;
		}
	}
};
public class ThreadCaseDemo02{
	public static void main(String args[]){
		Info info = new Info();	// 实例化Info对象
		Producer pro = new Producer(info) ;	// 生产者
		Consumer con = new Consumer(info) ;	// 消费者
		new Thread(pro).start() ;
		new Thread(con).start() ;
	}
};

在这里插入图片描述

问题2

这样修改之后,解决了消息错乱不对应的问题,但是,还是有消息重复取的问题;
该怎么解决这个消息重复取的问题呢?
既然有重复取,就会有重复设置的情况。
现在的代码,还并没有达到设置一个,取走一个的功能要求,那需要怎么解决呢?

问题再解决

分析一个机制:
1 图1:
如上图1:生产者生产一个产品以后,放入一个盒子;
当盒子是空的,盒子上方的灯是绿色,表示生产者可以向盒子里放产品;
2

图2:
入上图2,当产品放入盒子以后,灯从绿色变成红色,表示此时生产者不可以往里放产品了,但是,此时,消费者可以从里面取走产品;
3
图3:
如上图3,消费者从盒子取走产品以后,再将盒子的灯从红色置位绿色,表示消费者不可以从里面取了,但是生产者又可以往里面放产品了;那么,将产品2放入盒子,盒子的灯从绿色变成红色了,如下图4;

33
图4:

4
图5:
如上图5,但是,当产品2还没有被消费者取走,生产者的产品3就过来了的时候,发现是红灯,那么此时产品3得等待,等待消费者将产品2取走等变成绿色了再放入盒子。如下图6;
在这里插入图片描述
图6:
等变绿色,再将产品3放入盒子;

要想实现这样的机制,则需要依靠Object类中的方法的支持,什么方法呢???
Object类中存在一个等待与唤醒的机制,对线程等待与唤醒的控制;

Object类对线程的支持

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

不需要修改其他类,直接修改Info类即可,增加等待与唤醒的机制:
修改:

class Info{	// 定义信息类
	private String name = "李兴华";	 // 定义name属性
	private String content = "JAVA讲师"  ;		// 定义content属性
	private boolean flag = false ;	// 设置标志位
	public synchronized void set(String name,String content){
		if(!flag){
			try{
				super.wait() ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
		}
		this.setName(name) ;	// 设置名称
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		this.setContent(content) ;	// 设置内容
		flag  = false ;	// 改变标志位,表示可以取走
		super.notify() ;
	}
	public synchronized void get(){
		if(flag){
			try{
				super.wait() ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
		}
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		System.out.println(this.getName() + 
			" --> " + this.getContent()) ;
		flag  = true ;	// 改变标志位,表示可以生产
		super.notify() ;
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};
class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = false ;	// 定义标记位
		for(int i=0;i<50;i++){
			if(flag){
				this.info.set("李兴华","JAVA讲师") ;	// 设置名称
				flag = false ;
			}else{
				this.info.set("mldn","www.mldnjava.cn") ;	// 设置名称
				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<50;i++){
			this.info.get() ;
		}
	}
};
public class ThreadCaseDemo03{
	public static void main(String args[]){
		Info info = new Info();	// 实例化Info对象
		Producer pro = new Producer(info) ;	// 生产者
		Consumer con = new Consumer(info) ;	// 消费者
		new Thread(pro).start() ;
		new Thread(con).start() ;
	}
};

在这里插入图片描述
现在发现,实现了生产一条,取走一条,没有错乱,也没有重复取走了。此时,完成了生产者和消费者的正确操作。
在这里插入图片描述

线程的生命周期

1
一个新的线程创建以后可以通过start()方法进入运行状态,在运行状态可以通过yield()进行礼让,但是仍然可以执行,如果要让一个线程暂停的话,可以使用suspend(),sleep(),wait(),如果现在不需要线程再执行,则可以通过stop()停止线程,(run()方法执行结束也表示结束),一个新的线程直接调用stop()也可以进行结束;
但是,以下的几个方法都不再使用了,已经废弃了,因为他们都很容易引发死锁,所以都不建议使用了:
suspend()暂时挂起线程;
resume() 恢复挂起的线程;
stop() 结束线程;

那么,要结束一个线程,怎么操作?

可以通过设置标志位让线程停止;

class MyThread implements Runnable{
	private boolean flag = true ;	// 定义标志位
	public void run(){
		int i = 0 ;
		while(this.flag){
			System.out.println(Thread.currentThread().getName()
				+"运行,i = " + (i++)) ;
		}
	}
	public void stop(){
		this.flag = false ;	// 修改标志位
	}
};
public class StopDemo{
	public static void main(String args[]){
		MyThread my = new MyThread() ;
		Thread t = new Thread(my,"线程") ;	// 建立线程对象
		t.start() ;	// 启动线程
		try{
			Thread.sleep(30) ;
		}catch(Exception e){
			
		}
		my.stop() ;	// 修改标志位,停止运行
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值