java学习线程篇之生产者与消费者模式
1. 问题的引出
生产者和消费者指的是两个不同的线程类对象,操作同一资源的情况。具体的操作流程如下:
- 生产者负责生产数据,消费者负责取走数据;
- 生产者每生产完一组数据,消费者就要取走一组数据。
测试代码:
public class TestDemo{
public static void main(String[] args) {
Info info=new Info();
new Thread(new Producter(info)).start();
new Thread(new Consumer(info)).start();
}
}
class Info{
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
class Producter implements Runnable{
private Info info;
public Producter(Info info) {
this.info=info;
}
@Override
public void run() {
for (int i = 0; i <110 ; i++) {
if(i%2==0){
this.info.setTitle("重庆");
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
this.info.setContent("重庆大学");
}else{
this.info.setTitle("河南");
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
this.info.setContent("郑州大学");
}
}
}
}
class Consumer implements Runnable{
private Info info;
public Consumer(Info info) {
this.info=info;
}
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
try {
Thread.sleep(150);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(this.info.getTitle()+" ->"+this.info.getContent());
}
}
}
以上代码出现了两个严重问题:
- 数据错位,发现不再是一个所需要的完整数据(出现 重庆->郑州大学);
- 数据重复取出和数据重复设置。
2. 错误解决
2.1 解决数据错乱问题
数据的错误完全是因为非同步的操作造成的,应该使用同步处理。因为取和设置是两个不同的操作,如果想进行同步控制,那么就需要将其定义在一个类里面完成。
public class TestDemo{
public static void main(String[] args) {
Info info=new Info();
new Thread(new Producter(info)).start();
new Thread(new Consumer(info)).start();
}
}
class Info{
private String title;
private String content;
public synchronized void set(String title,String content){
this.title=title;
try {
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
this.content=content;
}
public synchronized void get(){
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(this.title+" ->"+this.content);
}
}
class Producter implements Runnable {
private Info info;
public Producter(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
this.info.set("重庆", "重庆大学");
} else {
this.info.set("河南", "郑州大学");
}
}
}
}
class Consumer implements Runnable{
private Info info;
public Consumer(Info info) {
this.info=info;
}
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
this.info.get();
}
}
}
此时数据的错误问题得到了很好的解决,但重复操作问题更加严重了。
2.2 解决重复的问题
如果要想实现整个代码的操作,必须加入等待机制与唤醒机制。在Object类里面提供有专门的处理方法:
-
等待
public final void wait(long timeout) throws InterruptedException
-
唤醒第一个等待线程:
public final void notify()
-
唤醒全部,哪个优先级高就先执行,
public final void notifyAll()
public class TestDemo{
public static void main(String[] args) {
Info info=new Info();
new Thread(new Producter(info)).start();
new Thread(new Consumer(info)).start();
}
}
class Info{
private String title;
private String content;
private boolean flag=true;//true表示可以生产,不可以取走;false表示可以取走,不可以生产
public synchronized void set(String title,String content){
if(this.flag==false){
try {
super.wait();
}catch (Exception e){
e.printStackTrace();
}
}
this.title=title;
try {
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
this.content=content;
this.flag=false;
super.notify();
}
public synchronized void get(){
if(this.flag==true){
try {
super.wait();
}catch (Exception e){
e.printStackTrace();
}
}
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(this.title+" ->"+this.content);
this.flag=true;
super.notify();
}
}
class Producter implements Runnable {
private Info info;
public Producter(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
this.info.set("重庆", "重庆大学");
} else {
this.info.set("河南", "郑州大学");
}
}
}
}
class Consumer implements Runnable{
private Info info;
public Consumer(Info info) {
this.info=info;
}
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
this.info.get();
}
}
}
面试题:请解释sleep()与wait()的区别?
- sleep()是Thread类的方法,而wait()是Object类的方法。
- sleep()可以设置休眠时间,时间一到自动唤醒,而wait()需要等待notify()进行唤醒。
- sleep()睡眠时,保持对象锁,仍然占有该锁;其他线程无法访问;而wait()睡眠时,释放对象锁。其他线程可以访问。