「java发布队列」java队列使用

博主:adminadmin 2022-11-29 04:51:08 60

今天给各位分享java发布队列的知识,其中也会对java队列使用进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

java中queue的使用方法?

java中的queue类是队列数据结构管理类。在它里边的元素可以按照添加它们的相同顺序被移除。

队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。不过优先级队列和 LIFO 队列(或堆栈)例外,前者根据提供的比较器或元素的自然顺序对元素进行排序,后者按 LIFO(后进先出)的方式对元素进行排序。无论使用哪种排序方式,队列的头都是调用remove()或poll()所移除的元素。在 FIFO 队列中,所有的新元素都插入队列的末尾。其他种类的队列可能使用不同的元素放置规则。每个Queue实现必须指定其顺序属性。

offer 添加一个元素并返回true 如果队列已满,则返回false

poll 移除并返问队列头部的元素 如果队列为空,则返回null

peek 返回队列头部的元素 如果队列为空,则返回null

put 添加一个元素 如果队列满,则阻塞

take 移除并返回队列头部的元素 如果队列为空,则阻塞

element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常

add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常

remove 移除并返回队列头部的元素 如果队列为空,则抛出一个

NoSuchElementException异常

注意:poll和peek方法出错进返回null。因此,向队列中插入null值是不合法的。

还有带超时的offer和poll方法重载,例如,下面的调用:

boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);

尝试在100毫秒内向队列尾部插入一个元素。如果成功,立即返回true;否则,当到达超时进,返回false。同样地,调用:

Object head = q.poll(100, TimeUnit.MILLISECONDS);

如果在100毫秒内成功地移除了队列头元素,则立即返回头元素;否则在到达超时时,返回null。

阻塞操作有put和take。put方法在队列满时阻塞,take方法在队列空时阻塞。

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接 口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

java中怎么实现队列

public class QueueE {

private Object[] data=null;

private int maxSize; //队列容量

private int front; //队列头,允许删除

private int rear; //队列尾,允许插入

//构造函数

public Queue(){

this(10);

}

public Queue(int initialSize){

if(initialSize =0){

this.maxSize = initialSize;

data = new Object[initialSize];

front = rear =0;

}else{

throw new RuntimeException("初始化大小不能小于0:" + initialSize);

}

}

//判空

public boolean empty(){

return rear==front?true:false;

}

//插入

public boolean add(E e){

if(rear== maxSize){

throw new RuntimeException("队列已满,无法插入新的元素!");

}else{

data[rear++]=e;

return true;

}

}

//返回队首元素,但不删除

public E peek(){

if(empty()){

throw new RuntimeException("空队列异常!");

}else{

return (E) data[front];

}

}

//出队

public E poll(){

if(empty()){

throw new RuntimeException("空队列异常!");

}else{

E value = (E) data[front]; //保留队列的front端的元素的值

data[front++] = null; //释放队列的front端的元素

return value;

}

}

//队列长度

public int length(){

return rear-front;

}

}

java 什么情况下使用 并发队列

并发队列是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael

Scott算法上进行了一些修改。

入队列

入队列就是将入队节点添加到队列的尾部。为了方便理解入队时队列的变化,以及head节点和tair节点的变化,每添加一个节点我就做了一个队列的快照图。

第一步添加元素1。队列更新head节点的next节点为元素1节点。又因为tail节点默认情况下等于head节点,所以它们的next节点都指向元素1节点。

第二步添加元素2。队列首先设置元素1节点的next节点为元素2节点,然后更新tail节点指向元素2节点。

第三步添加元素3,设置tail节点的next节点为元素3节点。

第四步添加元素4,设置元素3的next节点为元素4节点,然后将tail节点指向元素4节点。

通过debug入队过程并观察head节点和tail节点的变化,发现入队主要做两件事情,

第一是将入队节点设置成当前队列尾节点的下一个节点。

第二是更新tail节点,如果tail节点的next节点不为空,则将入队节点设置成tail节点,如果tail节点的next节点为空,则将入队节点设置成tail的next节点,所以tail节点不总是尾节点,理解这一点对于我们研究源码会非常有帮助。

上面的分析让我们从单线程入队的角度来理解入队过程,但是多个线程同时进行入队情况就变得更加复杂,因为可能会出现其他线程插队的情况。如果有一个线程正在入队,那么它必须先获取尾节点,然后设置尾节点的下一个节点为入队节点,但这时可能有另外一个线程插队了,那么队列的尾节点就会发生变化,这时当前线程要暂停入队操作,然后重新获取尾节点。让我们再通过源码来详细分析下它是如何使用CAS算法来入队的。

public boolean offer(E e) {    

          

   if (e == null) throw new NullPointerException();    

         

    //入队前,创建一个入队节点    

         

    Node/ee n = new Node/ee(e);    

  

   retry:    

          

    //死循环,入队不成功反复入队。    

          

    for (;;) {    

           

        //创建一个指向tail节点的引用    

           

       Node/ee t = tail;    

           

     //p用来表示队列的尾节点,默认情况下等于tail节点。    

           

      Node/ee p = t;    

           

      for (int hops = 0; ; hops++) {    

           

     //获得p节点的下一个节点。    

           

        Node/ee next = succ(p);    

           

     //next节点不为空,说明p不是尾节点,需要更新p后在将它指向next节点    

           

        if (next != null) {    

           

     //循环了两次及其以上,并且当前节点还是不等于尾节点    

           

        if (hops  HOPS  t != tail)    

           

            continue retry;    

           

            p = next;    

           

        }    

           

     //如果p是尾节点,则设置p节点的next节点为入队节点。    

           

         else if (p.casNext(null, n)) {    

           

        //如果tail节点有大于等于1个next节点,则将入队节点设置成tair节点,更新失败了也没关系,因为失败了表示有其他线程成功更新了tair节点。    

           

    if (hops = HOPS)    

           

           casTail(t, n); // 更新tail节点,允许失败    

           

        return true;    

           

         }    

           

        // p有next节点,表示p的next节点是尾节点,则重新设置p节点    

           

        else {    

           

          p = succ(p);    

           

        }    

           

     }    

         

  }    

          

}

从源代码角度来看整个入队过程主要做二件事情。

第一步定位尾节点。tail节点并不总是尾节点,所以每次入队都必须先通过tail节点来找到尾节点,尾节点可能就是tail节点,也可能是tail节点的next节点。代码中循环体中的第一个if就是判断tail是否有next节点,有则表示next节点可能是尾节点。获取tail节点的next节点需要注意的是p节点等于p的next节点的情况,只有一种可能就是p节点和p的next节点都等于空,表示这个队列刚初始化,正准备添加第一次节点,所以需要返回head节点。获取p节点的next节点代码如下

final Node/ee succ(Node/ee p) {    

        

     Node/ee next = p.getNext();    

 

       return (p == next) ? head : next;    

        

      }

第二步设置入队节点为尾节点。p.casNext(null, n)方法用于将入队节点设置为当前队列尾节点的next节点,p如果是null表示p是当前队列的尾节点,如果不为null表示有其他线程更新了尾节点,则需要重新获取当前队列的尾节点。

hops的设计意图。上面分析过对于先进先出的队列入队所要做的事情就是将入队节点设置成尾节点,doug lea写的代码和逻辑还是稍微有点复杂。那么我用以下方式来实现行不行?

public boolean offer(E e) {    

      

    if (e == null)    

           

       throw new NullPointerException();    

          

      Node/ee n = new Node/ee(e);    

          

          for (;;) {    

          

      Node/ee t = tail;    

         

       if (t.casNext(null, n)  casTail(t, n)) {    

          

             return true;    

          

     }    

         

     }    

         

}

让tail节点永远作为队列的尾节点,这样实现代码量非常少,而且逻辑非常清楚和易懂。但是这么做有个缺点就是每次都需要使用循环CAS更新tail节点。如果能减少CAS更新tail节点的次数,就能提高入队的效率,所以doug

lea使用hops变量来控制并减少tail节点的更新频率,并不是每次节点入队后都将 tail节点更新成尾节点,而是当

tail节点和尾节点的距离大于等于常量HOPS的值(默认等于1)时才更新tail节点,tail和尾节点的距离越长使用CAS更新tail节点的次数就会越少,但是距离越长带来的负面效果就是每次入队时定位尾节点的时间就越长,因为循环体需要多循环一次来定位出尾节点,但是这样仍然能提高入队的效率,因为从本质上来看它通过增加对volatile变量的读操作来减少了对volatile变量的写操作,而对volatile变量的写操作开销要远远大于读操作,所以入队效率会有所提升。

 private static final int HOPS = 1;

还有一点需要注意的是入队方法永远返回true,所以不要通过返回值判断入队是否成功。

4. 出队列

出队列的就是从队列里返回一个节点元素,并清空该节点对元素的引用。让我们通过每个节点出队的快照来观察下head节点的变化。

从上图可知,并不是每次出队时都更新head节点,当head节点里有元素时,直接弹出head节点里的元素,而不会更新head节点。只有当head节点里没有元素时,出队操作才会更新head节点。这种做法也是通过hops变量来减少使用CAS更新head节点的消耗,从而提高出队效率。让我们再通过源码来深入分析下出队过程。

 public E poll() {    

        

    Node/ee h = head;    

       

    // p表示头节点,需要出队的节点    

        

      Node/ee p = h;    

        

      for (int hops = 0;; hops++) {    

        

         // 获取p节点的元素    

        

         E item = p.getItem();    

        

         // 如果p节点的元素不为空,使用CAS设置p节点引用的元素为null,如果成功则返回p节点的元素。    

        

         if (item != null  p.casItem(item, null)) {    

         

         if (hops = HOPS) {    

        

              //将p节点下一个节点设置成head节点    

        

              Node/ee q = p.getNext();    

        

              updateHead(h, (q != null) ? q : p);    

       

              }    

        

                  return item;    

 

              }    

        

        // 如果头节点的元素为空或头节点发生了变化,这说明头节点已经被另外一个线程修改了。那么获取p节点的下一个节点    

        

        Node/ee next = succ(p);    

        

        // 如果p的下一个节点也为空,说明这个队列已经空了    

       

        if (next == null) {    

        

       // 更新头节点。    

         

             updateHead(h, p);    

        

             break;    

         

             }    

        

      // 如果下一个元素不为空,则将头节点的下一个节点设置成头节点    

         

       p = next;    

         

      }    

        

          return null;    

        

      }

首先获取头节点的元素,然后判断头节点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作将该节点的元素取走,如果不为空,则使用CAS的方式将头节点的引用设置成null,如果CAS成功,则直接返回头节点的元素,如果不成功,表示另外一个线程已经进行了一次出队操作更新了head节点,导致元素发生了变化,需要重新获取头节点。

JAVA如何用队列实现并发?

如果是抢资源,在不作弊的情况下

按照先来先得的规则

,那么比较简单的实现就是队列

,不管请求的并发多高,如果用线程来实现为用户服务,也就是说

来一个人请求资源那么就启动一个线程,那CPU执行线程总是有顺序的,比如

当前三个人(路人甲路人乙路人丙)请求A资源

,那服务端就起了三个线程为这三个人服务,假设

这三个人不太幸运在请求的时候没有及时的获得CPU时间片,那么他们三个相当于公平竞争CPU资源,而CPU选择运行线程是不确定顺序的

,又假设

选中了路人丙的线程运行那么将其放入队列就好了,路人乙,路人丙以此类推

,那可能会想为什么不及时的处理呢

,因为后续的操作可能是耗时操作对于线程的占用时间较长那请求资源的人多了服务端就可能挂了

到底什么是消息队列?Java中如何实现消息队列

消息队列,顾名思义

首先是个队列。

队列的操作有入队和出队

也就是你有一个程序在产生内容然后入队(生产者)

另一个程序读取内容,内容出队(消费者)

这是最最基本的概念。

我想你应该是缺乏一个使用场景。

当你不需要立即获得结果,但是并发量又不能无限大的时候,差不多就是你需要使用消息队列的时候。

比如你写日志,因为可能一个客户端有多个操作去写,又有很多个客户端,显然并发不能无穷大,于是你就需要把写日志的请求放入到消息队列里,在消费者那边依次把队列中产生的日志写到数据库里。

至于怎么实现消息队列,其实你本身一个普通的队列就行呀~看你需要什么附加功能而已。

怎样用java代码实现一个队列

class StackT {

private VectorT v;

public Stack(){

v = new VectorT();

}

public T pop(){

if (v.size()==0) return null;

return v.get(v.size()-1);

}

public void push(T t){

v.add(t);

}

public boolean isEmpty(){

return v.size()==0;

}

}

class QueueT{

private VectorT v;

public Queue(){

v = new VectorT();

}

//入队列

public void enqueue(T t){

v.add(t);

}

//出队列

public T dequeue(){

if (v.size()==0) return null;

return v.get(0);

}

public boolean isEmpty(){

return v.size() == 0;

}

}

关于java发布队列和java队列使用的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-11-29,除非注明,否则均为首码项目网原创文章,转载请注明出处。