生产者与消费者经典小案例

末鹿安然 提交于 2020-02-04 18:10:43

今天给大家分享一下一个经典的案例,供给他人学习以及自己今天的回首展望,首先让我们先看一下需求:
1.并行生成任务,id是唯一,不能重复
2.并行消费,id按照升序打印出来
3.精确任务到200个,并计算出消费时间

首先根据需求分析,我们首先来创建一个实体类,该实体类只有一个id属性,并提供get set方法和构造函数

public class Task {

private Integer id;

public Task(Integer id) {
    this.id = id;
}

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

}

然后创建生产者和消费者的对应的接口

//生产者
public interface Prodeucer {

void produce() throws InterruptedException;

}

//消费者
public interface Consumer {

void consumer()throws InterruptedException;

}

根据最近所研究的模板模式,正好这个案例就将他实用上,创建生产者和消费者的接口实现类

//生产者接口实现类
public abstract class AbstaractProducer implements Prodeucer,Runnable {

@Override
public void run() {
    while (true){
        try {
            produce();
        }catch (InterruptedException e){
            e.printStackTrace();
            break;
        }
    }
}

}

//消费者接口实现类
public abstract class AbstaractConsumer implements Consumer,Runnable {

@Override
public void run() {

    for(int i=0;i<=199;i++){

        try {
            consumer();
        }catch (InterruptedException e){
            e.printStackTrace();
            break;
        }
    }
    Test.countDownLatch.countDown();
}

}

注意,大家所看到的Test.countDownLatch.countDown()方法是用来回到Main方法继续执行余下的代码,目的是用来计算消费200个ID所花费的时间,后面会再跟大家提到

接着我们还需要创建生产者和消费者的总接口,里面存放关键代码的方法

public interface Model {

Runnable newRunnableCounsumer();
Runnable newRunnableProducer();

}

然后我们就需要去创建实现这个接口的类

public class BlockingQueueModel implements Model {

private final BlockingQueue<Task> queue;

private final Set<Integer> set= Collections.synchronizedSet(new HashSet<Integer>());

private final AtomicInteger atomicInteger=new AtomicInteger(0);

private final Lock putlock=new ReentrantLock();

private final Lock getlock=new ReentrantLock();

private static  int id=0;

public BlockingQueueModel(int cap) {

    this.queue = new LinkedBlockingQueue<Task>(cap);
}


@Override
public Runnable newRunnableCounsumer()
{
    return new ConsumerImpl();
}

@Override
public Runnable newRunnableProducer() {

    return  new ProducerImpl();
}





//内部类
public class ProducerImpl extends   AbstaractProducer {

    @Override
    public void produce() throws InterruptedException {
    
           //排序不重复
           putlock.lock();
           Task task=new Task(atomicInteger.getAndIncrement());
            if (task.getId()>=200){
                Thread.currentThread().interrupt();
            }
            if (!Thread.currentThread().isInterrupted()){
                queue.put(task);

            }
            putlock.unlock();


    }
}




private class ConsumerImpl extends AbstaractConsumer{


    @Override
    public void consumer() throws InterruptedException {


            getlock.lock();
            Task task=queue.take();
            if (set.contains(task.getId())){
                System.err.println("有重复的id:"+task.getId());
            }
            set.add(task.getId());
            System.err.println("ID是:"+task.getId());
            Thread.sleep((long)(Math.random()*500));
            getlock.unlock();

    }
}

}

这里运用到BlockingQueue和AtomicInteger就是为了确保ID的唯一性,不会重复,并且能够按照升序进行输出,使用判断控制ID只生产到200个

接下来我们就需要编写main方法实现对消费的计算,以及运行程序了

public class Test {

static  final CountDownLatch countDownLatch=new CountDownLatch(1);

public static void main(String[] args) {
      System.err.println("-------------start---------------------");
      long startTime = System.currentTimeMillis(); // 获取开始时间
      Model model=new BlockingQueueModel(1);
      new Thread(model.newRunnableProducer()).start();
      new Thread(model.newRunnableCounsumer()).start();
    try {
        countDownLatch.await();
        long endTime = System.currentTimeMillis(); // 获取结束时间
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        System.err.println("程序开始执行时间:"+startTime);
        System.out.println("程序结束执行时间:"+endTime);
        System.out.println(df.format(new Date()));// new Date()为获取当前系统时间
        System.out.println("程序总运行时间: " + (endTime - startTime) + "ms");
    }catch (Exception e){

    }finally {
        System.exit(0);
    }
}

}

这里要用到CountDownLatch用来计算消费ID耗时,注意这里的线程最好只创建一个,否则的话还需要修改 AbstaractProducer抽象类的代码,在程序开始时开始计算时间,然后分别创建生产者和消费者进行开始,在下面的代码一定要 加上countDownLatch.await();,不然主程序会直接跑完,计算完时间后,需要手动关闭程序 System.exit(0);因为暂时没想到更好的解决办法,所以就等待后期优化了.

整体目录结构

在这里插入图片描述

看到这里非常感谢你浪费诸多时间,如果对你有帮助的话,点个赞吧

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!