synchronized实现的生产者消费者模式

来源:互联网 发布:宁德catl知乎 编辑:程序博客网 时间:2024/06/10 18:16

生产者-消费者

  一个或多个生产者创建某些工作并将其置于缓冲区或队列中,一个或多个消费者会从队列中获得这些工作并完成之。这里的缓冲区或队列是临界资源。当缓冲区或队列放满的时候,生产这会被阻塞;而缓冲区或队列为空的时候,消费者会被阻塞。生产者和消费者的调度是通过二者相互交换信号完成的

这里用到了线程的并发,和同步机制,同步锁lock的使用,wait,notify的使用:
代码如下:
package lockDemo;


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class SynchronizedDemo {
public static void main(String[] args) {
//利用Lock接口和ReentrantLock类创建一个锁,作为对象唯一的标志,锁标志
Lock lock=new ReentrantLock();
//创建共享资源对象方法
Resource resource=new Resource("唛头", lock, 5);
//创建生产者线程和消费者对象线程
Productor productor=new Productor(resource);
Customer customer=new Customer(resource);
Thread thread1=new Thread(productor, "SA");
Thread thread2=new Thread(customer, "SB");
//线程启动
thread1.start();
thread2.start();

}
}
class Resource{
private String name;
private int count;//用来记录生产和消费后库存的馒头数
private boolean has=false;//保证count的安全性,数据准确性
private Lock lock;//锁对象
public Resource() {}//无参构造方法
public Resource(String name,Lock lock,int count) {//有参的构造方法,主要是传入锁对象
this.name=name;
this.lock=lock;
this.count=count;
}
//存放,生产者生产放入馒头的方法
public void push(int num) {
while(true) {//保证循环执行
synchronized (lock) {//同步代码块和同步锁对象
lock.notifyAll();//唤醒所以其它正在等待该该锁的线程
if (!has && (count+num)>=20) {//双重判定保证数据准确
try {
lock.wait();//让拥有该锁的对象睡眠,阻塞,注意必须用在同步块中
Thread.sleep(1000);//让当前线程睡眠1秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//这里将判断标志has至为原值,便于下一轮判断使用,避免因进入取走状态时候发生变化
has=false;//不可少


}else {
count+=num;
System.out.println(Thread.currentThread().getName()+"放入:"+num+" 个");
System.out.println("当前:"+count);
//这里将判断标志has至为原值,便于下一轮判断使用
has=false;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
//外取,消费者消费取走馒头方法
public void popup(int num) {
while(true) {
//这里用到has原因是为了避免两个线程都到了这里,并且此时has都是true是发生混乱
synchronized (lock) {
lock.notifyAll();
if (has && (count-num)<=0) {
try {
lock.wait();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
has=true;

}else {
count-=num;
System.out.println(Thread.currentThread().getName()+"取走:"+num+" 个");
System.out.println("剩余"+count);
has=true;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
//其它没用枷锁的方法,显示了同步锁不会影响其它没有共享资源的方法
public void show() {
System.out.println("傻帽");
}
}


class Productor implements Runnable{
Resource resource;
public Productor() {}
public Productor(Resource resource) {this.resource=resource;}
@Override
public void run() {
//进入这里时候是随机的,先执行哪个线程的不一定,但是即某一个线程而言,一定是先执行方法show(),然后在两个线程的push和popup之间切换
resource.show();
resource.push((int)(Math.random()*5+1));
}

}
class Customer implements Runnable{
Resource resource;
public Customer() {}
public Customer(Resource resource) {this.resource=resource;}
@Override
public void run() {
//这里可以用while或for将方法包围,实现多次方法调用,也可以不用,因为方法本身带有循环题
resource.show();
//设置取走馒头的数量为随机的
int ran=(int)(Math.random()*5+1);
resource.popup(ran);
}

}
//这里要注意的是同步锁一定要放在资源共享的区域,共享的对象,类中都行,同时要保证所得唯一性

原创粉丝点击