广告
返回顶部
首页 > 资讯 > 后端开发 > Python >浅谈Java中的Queue家族
  • 1063
分享到

浅谈Java中的Queue家族

2024-04-02 19:04:59 1063人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录Queue接口Queue的分类BlockingQueueDequeTransferQueueQueue接口 先看下Queue的继承关系和其中定义的方法: Queue继承自Col

Queue接口

先看下Queue的继承关系和其中定义的方法:

Queue继承自Collection,Collection继承自Iterable。

Queue有三类主要的方法,我们用个表格来看一下他们的区别:

方法类型 方法名称 方法名称 区别
Insert add offer 两个方法都表示向Queue中添加某个元素,不同之处在于添加失败的情况,add只会返回true,如果添加失败,会抛出异常。offer在添加失败的时候会返回false。所以对那些有固定长度的Queue,优先使用offer方法。
Remove remove poll 如果Queue是空的情况下,remove会抛出异常,而poll会返回null。
Examine element peek 获取Queue头部的元素,但不从Queue中删除。两者的区别还是在于Queue为空的情况下,element会抛出异常,而peek返回null。

注意,因为对poll和peek来说null是有特殊含义的,所以一般来说Queue中禁止插入null,但是在实现中还是有一些类允许插入null比如LinkedList。

尽管如此,我们在使用中还是要避免插入null元素。

Queue的分类

一般来说Queue可以分为BlockingQueue,Deque和TransferQueue三种。

BlockingQueue

BlockingQueue是Queue的一种实现,它提供了两种额外的功能:

当当前Queue是空的时候,从BlockingQueue中获取元素的操作会被阻塞。当当前Queue达到最大容量的时候,插入BlockingQueue的操作会被阻塞。

BlockingQueue的操作可以分为下面四类:

操作类型Throws exceptionSpecial valueBlocksTimes outInsertadd(e)offer(e)put(e)offer(e, time, unit)Removeremove()poll()take()poll(time, unit)Examineelement()peek()not applicablenot applicable

第一类是会抛出异常的操作,当遇到插入失败,队列为空的时候抛出异常。

第二类是不会抛出异常的操作。

第三类是会Block的操作。当Queue为空或者达到最大容量的时候。

第四类是time out的操作,在给定的时间里会Block,超时会直接返回。

BlockingQueue是线程安全的Queue,可以在生产者消费者模式的多线程中使用,如下所示:


class Producer implements Runnable {
   private final BlockingQueue queue;
   Producer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { queue.put(produce()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   Object produce() { ... }
 }

 class Consumer implements Runnable {
   private final BlockingQueue queue;
   Consumer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { consume(queue.take()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   void consume(Object x) { ... }
 }

 class Setup {
   void main() {
     BlockingQueue q = new SomeQueueImplementation();
     Producer p = new Producer(q);
     Consumer c1 = new Consumer(q);
     Consumer c2 = new Consumer(q);
     new Thread(p).start();
     new Thread(c1).start();
     new Thread(c2).start();
   }
 }

最后,在一个线程中向BlockQueue中插入元素之前的操作happens-before另外一个线程中从BlockQueue中删除或者获取的操作。

Deque

Deque是Queue的子类,它代表double ended queue,也就是说可以从Queue的头部或者尾部插入和删除元素。

同样的,我们也可以将Deque的方法用下面的表格来表示,Deque的方法可以分为对头部的操作和对尾部的操作:

方法类型 Throws exception Special value Throws exception Special value
Insert addFirst(e) offerFirst(e) addLast(e) offerLast(e)
Remove removeFirst() pollFirst() removeLast() pollLast()
Examine getFirst() peekFirst() getLast() peekLast()

和Queue的方法描述基本一致,这里就不多讲了。

当Deque以 FIFO (First-In-First-Out)的方法处理元素的时候,Deque就相当于一个Queue。

当Deque以LIFO (Last-In-First-Out)的方式处理元素的时候,Deque就相当于一个Stack。

TransferQueue

TransferQueue继承自BlockingQueue,为什么叫Transfer呢?因为TransferQueue提供了一个transfer的方法,生产者可以调用这个transfer方法,从而等待消费者调用take或者poll方法从Queue中拿取数据。

还提供了非阻塞和timeout版本的tryTransfer方法以供使用。

我们举个TransferQueue实现的生产者消费者的问题。

先定义一个生产者:


@Slf4j
@Data
@AllArgsConstructor
class Producer implements Runnable {
    private TransferQueue<String> transferQueue;

    private String name;

    private Integer messageCount;

    public static final AtomicInteger messageProduced = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 0; i < messageCount; i++) {
            try {
                boolean added = transferQueue.tryTransfer( "第"+i+"个", 2000, TimeUnit.MILLISECONDS);
                log.info("transfered {} 是否成功: {}","第"+i+"个",added);
                if(added){
                    messageProduced.incrementAndGet();
                }
            } catch (InterruptedException e) {
                log.error(e.getMessage(),e);
            }
        }
        log.info("total transfered {}",messageProduced.get());
    }
}

在生产者的run方法中,我们调用了tryTransfer方法,等待2秒钟,如果没成功则直接返回。

再定义一个消费者:


@Slf4j
@Data
@AllArgsConstructor
public class Consumer implements Runnable {

    private TransferQueue<String> transferQueue;

    private String name;

    private int messageCount;

    public static final AtomicInteger messageConsumed = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 0; i < messageCount; i++) {
            try {
                String element = transferQueue.take();
                log.info("take {}",element );
                messageConsumed.incrementAndGet();
                Thread.sleep(500);
            } catch (InterruptedException e) {
                log.error(e.getMessage(),e);
            }
        }
        log.info("total consumed {}",messageConsumed.get());
    }

}

在run方法中,调用了transferQueue.take方法来取消息。

下面先看一下一个生产者,零个消费者的情况:


@Test
public void testOneProduceZeroConsumer() throws InterruptedException {

    TransferQueue<String> transferQueue = new LinkedTransferQueue<>();
    ExecutorService exService = Executors.newFixedThreadPool(10);
    Producer producer = new Producer(transferQueue, "ProducerOne", 5);

    exService.execute(producer);

    exService.awaitTermination(50000, TimeUnit.MILLISECONDS);
    exService.shutdown();
}

输出结果:

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第0个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第1个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第2个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第3个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第4个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - total transfered 0

可以看到,因为没有消费者,所以消息并没有发送成功。

再看下一个有消费者的情况:


@Test
public void testOneProduceOneConsumer() throws InterruptedException {

    TransferQueue<String> transferQueue = new LinkedTransferQueue<>();
    ExecutorService exService = Executors.newFixedThreadPool(10);
    Producer producer = new Producer(transferQueue, "ProducerOne", 2);
    Consumer consumer = new Consumer(transferQueue, "ConsumerOne", 2);

    exService.execute(producer);
    exService.execute(consumer);

    exService.awaitTermination(50000, TimeUnit.MILLISECONDS);
    exService.shutdown();
}

输出结果:

[pool-1-thread-2] INFO com.flydean.Consumer - take 第0个

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第0个 是否成功: true

[pool-1-thread-2] INFO com.flydean.Consumer - take 第1个

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第1个 是否成功: true

[pool-1-thread-1] INFO com.flydean.Producer - total transfered 2

[pool-1-thread-2] INFO com.flydean.Consumer - total consumed 2

可以看到Producer和Consumer是一个一个来生产和消费的。

以上就是浅谈Java中的Queue家族的详细内容,更多关于Java中的Queue家族的资料请关注编程网其它相关文章!

--结束END--

本文标题: 浅谈Java中的Queue家族

本文链接: https://www.lsjlt.com/news/127341.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅谈Java中的Queue家族
    目录Queue接口Queue的分类BlockingQueueDequeTransferQueueQueue接口 先看下Queue的继承关系和其中定义的方法: Queue继承自Col...
    99+
    2022-11-12
  • 浅谈JS的二进制家族
    目录概述BlobBlob实战Blob下载文件Blob图片本地显示Blob文件分片上传本地读取文件内容ArrayBuffer通过ArrayBuffer的格式读取本地数据通过ArrayB...
    99+
    2022-11-12
  • Java Springboot之Spring家族的技术体系
    目录一、Why二、Spring 家族技术生态全景图三、Spring Framework 的整体架构四、Spring Boot 与 Web 应用程序五、支持运行期内嵌容器(传统 Web...
    99+
    2022-11-12
  • 浅谈Java中FastJson的使用
    FastJson的使用 使用maven导入依赖包 <!--下边依赖跟aop没关系,只是项目中用到了 JSONObject,所以引入fastjson--> <de...
    99+
    2022-11-12
  • 浅谈Java中的内部类
    最近在讲Java中的内部类,感觉内部类实际上对于初学者用得不多,那么内部类简单的说,其实就是在一个类的内部定义的类。按照定义的情况分为:成员内部类,局部内部类,静态内部类,匿名内部类。成员内部类,就是定义一个当作类的成员变量的类。局部内部类...
    99+
    2023-06-02
  • 浅谈 Java 中 this 的使用(转)
    浅谈 Java 中 this 的使用(转)[@more@]  1. this是指当前对象自己。  当在一个类中要明确指出使用对象自己的的变量或函数时就应该加上this引用。如下面这个例子中:CODE:...
    99+
    2023-06-03
  • 简单谈谈python中的Queue与多进程
    最近接触一个项目,要在多个虚拟机中运行任务,参考别人之前项目的代码,采用了多进程来处理,于是上网查了查python中的多进程 一、先说说Queue(队列对象) Queue是python中的标准库,可以直接i...
    99+
    2022-06-04
    进程 简单 python
  • Java Springboot之Spring家族的技术体系是什么
    这篇文章主要介绍了Java Springboot之Spring家族的技术体系是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、WhySpring Boot 在传统 Sp...
    99+
    2023-06-15
  • 浅谈Java ThreadPoolExecutor的使用
    目录一、前言二、ThreadPoolExecutor三、构造函数参数解析四、总结一、前言 线程池主要由以下4个核心组件组成。 线程池管理器:用于创建并管理线程池 工...
    99+
    2022-11-12
  • 浅谈Java中Lock和Synchronized的区别
    目录1. 从功能角度来看2. 从特性来看3. 从性能方面来看1. 从功能角度来看 Lock和Synchronized都是java中去用来解决线程安全问题的一个工具 2. 从特性来看 ...
    99+
    2022-11-13
  • 浅谈Java中String的常用方法
    String中常用的方法,我以代码的形式,来说明这些常用的方法。 @Test public void test1(){ //1.返回字符串的长度 ...
    99+
    2022-11-12
  • 浅谈一下Java中的堆和栈
    Java数据类型在执行过程中存储在两种不同形式的内存中:栈和堆,它们通常由运行Java虚拟机(JVM)的底层平台维护。本文从Java软件开发的角度提供了对这两种内存类型的一些见解。 ...
    99+
    2023-05-18
    Java Java
  • 浅谈java对象的比较
    目录1、元素的比较2、类的比较3、比较方法3.1 重写equals方法3.2 基于Comparble接口类的比较3.3 基于比较器比较基于比较器比较:Comparator接口3.4 ...
    99+
    2022-11-12
  • 浅谈Java中replace与replaceAll区别
    在Java中,replace和replaceAll都是用于替换字符串中的字符或字符串的方法,但它们之间有一些区别。1. 参数类型:-...
    99+
    2023-08-14
    Java
  • 浅谈java中String相关问题
    原文:https://blog.csdn.net/qq_41268447/article/details/96759597    首先我们先说一下java堆内存和栈内存 java中八个基本数据类型就是值类型,存放在栈内存...
    99+
    2023-06-02
  • 浅谈Java编程中的synthetic关键字
    java synthetic关键字。有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field。小颖编译工具用的就是jad.所有反编译工具都不能保证完全正确地反编译clas...
    99+
    2023-05-31
    java synthetic 关键字
  • 浅谈一下Java中枚举的用法
    目录枚举(enum)定义一个季节的枚举类枚举类values()方法ordinal()方法 valueOf()方法枚举类成员枚举(enum) 枚举是一个被命名的整型常数的集合...
    99+
    2023-05-14
    Java枚举 Java枚举的用法
  • 浅谈java中HashMap键的比较方式
    先看一个例子 Integer integer=12344; Integer integer1=12344; 在Java中Integer 和Integer1是不相等的,但是...
    99+
    2022-11-12
  • 浅谈Java中Properties类的详细使用
    目录一、Properties 类二、打印JVM参数三、打印自定义.properties文件中的值3.1、list输出到控制台用绝对路径加载3.2、propertyNames输出get...
    99+
    2022-11-12
  • 浅谈Java中static和非static的区别
    关于static和非static变量的区别 static 修饰的变量称为类变量或全局变量或成员变量,在类被加载的时候成员变量即被初始化,与类关联,只要类存在,static变量就存在。非static修饰的成员变量是在对象new出来的时候划分存...
    99+
    2023-05-31
    java static ava
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作