今天给大家分享一下一个经典的案例,供给他人学习以及自己今天的回首展望,首先让我们先看一下需求:
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);因为暂时没想到更好的解决办法,所以就等待后期优化了.
整体目录结构
看到这里非常感谢你浪费诸多时间,如果对你有帮助的话,点个赞吧
来源:CSDN
作者:巅峰的宿敌
链接:https://blog.csdn.net/SuDiZ/article/details/104170073