并发队列 ConcurrentLinkedQueue 及 BlockingQueue 接口实现的四种队列

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a78270528/article/details/79737661

队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将是最后被删除的元素,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。

在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。


一、ConcurrentLinkedQueue

是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLikedQueue性能好于BlockingQueue。

它是一个基于连接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。

ConcurrentLinkedQueue重要方法:

add()和offer()都是加入元素的方法(在ConcurrentLinkedQueue中,这两个方法没有任何区别)。

poll()和peek()都是取头元素节点,区别在于前者会删除元素,后者不会。

下面看一个例子:

  /* 
   * 一个基于链接节点的、无界的、线程安全的队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 
   * 是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共 collection 
   * 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许 null 元素。 
   */    
  private void concurrentLinkedQueueTest() {    
      ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();    
      concurrentLinkedQueue.add("a");    
      concurrentLinkedQueue.add("b");    
      concurrentLinkedQueue.add("c");    
      concurrentLinkedQueue.offer("d"); // 将指定元素插入到此队列的尾部。    
      concurrentLinkedQueue.peek(); // 检索并移除此队列的头,如果此队列为空,则返回 null。    
      concurrentLinkedQueue.poll(); // 检索并移除此队列的头,如果此队列为空,则返回 null。    
  
      for (String str : concurrentLinkedQueue) {    
          System.out.println(str);    
      }    
  }  

注意:ConcurrentLinkedQueue的API.size() 是要遍历一遍集合的,速很慢,所以判空时,尽量要避免用size(),而改用isEmpty()。

二、BlockingQueue接口

ArrayBlockingQueue:基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部没实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者先进后出,也叫有界队列,在很多场合非常适合使用。

package concurrent;  
  
import java.util.concurrent.ArrayBlockingQueue;  
import java.util.concurrent.TimeUnit;  
  
public class ArrayBlockingQueueTest {  
  
    private ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(10);  
  
    public static void main(String[] args) throws InterruptedException {  
        final ArrayBlockingQueueTest arrayBlockingQueueTest = new ArrayBlockingQueueTest();  
        new Thread(new Runnable() {  
            public void run() {  
                try {  
                    arrayBlockingQueueTest.producer();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
        new Thread(new Runnable() {  
            public void run() {  
                try {  
                    arrayBlockingQueueTest.consumer();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
  
  
    }  
  
    private void producer() throws InterruptedException {  
        for(int i=0; i<100; i++) {  
            System.out.println("arrayBlockingQueue.size()="+arrayBlockingQueue.size());  
            //Thread.sleep(1000);  
            //队列满了之后会直接抛出异常  
            //arrayBlockingQueue.add(i);  
            //队列满了之后会等待队列腾出空间  
            //arrayBlockingQueue.put(i);  
            //将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。  
            arrayBlockingQueue.offer(i);  
  
        }  
    }  
  
    private void consumer() throws InterruptedException {  
        while(true) {  
            //Thread.sleep(1000);  
            //获取并移除此队列的头部,在指定的等待时间前等待可用的元素。如果已经没有可用的元素,则没10s返回一个null  
            // System.out.println(arrayBlockingQueue.poll(10000, TimeUnit.MILLISECONDS));  
            //获取并移除此队列的头部,在元素变得可用之前一直等待  
            System.out.println(arrayBlockingQueue.take());  
            //获取但不移除此队列的头;如果此队列为空,则返回 null  
            //System.out.println(arrayBlockingQueue.peek());  
        }  
    }  
}

关于队列中各方法的说明:

操作抛出异常返回个特殊值阻塞到队列可用一定时间后退出操作方式
添加元素add(e)offer(e)put(e)offer(e,time,unit)添加到队尾
移除元素remove()poll()take()poll(e,time,unit)获取头元素并移除
查询元素element()peek()获取头元素不移除

LinkedBlockingQueue:基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也是维护着一个数据缓冲队列(该队列有一个链表构成),LinkedBlockingQueue之所以能够高效的处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者操作的完全并行运行。它是一个无界队列。

private LinkedBlockingQueue<String> queue;//礼物的队列
private final static int GET_QUEUE_GIFT = 0;//从队列中获取礼物
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case GET_QUEUE_GIFT://如果是从队列中获取礼物实体的消息
                if (!queue.isEmpty()) {
                    String vo = queue.poll();
                    if (vo != null) {//如果从队列中获取的礼物不为空,那么就将礼物展示在界面上
                        Log.e("------", "------获取的------" + vo);
                        handler.sendEmptyMessageDelayed(GET_QUEUE_GIFT, 1000);
                    }
                } else {//如果这次从队列中获取的消息是礼物是空的,则一秒之后重新获取
                    Log.e("------", "------获取的------isEmpty");
                }
                break;
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.addqueue).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            for (int i = 0; i < 6; i++) {
                queue.add("我是队列中的-----第" + (i + 6) + "个");
            }
            handler.sendEmptyMessageDelayed(GET_QUEUE_GIFT, 1000);//轮询队列获取礼物
        }
    });
    queue = new LinkedBlockingQueue<>();
    for (int i = 0; i < 6; i++) {
        queue.add("我是队列中的第" + i + "个");
    }
    handler.sendEmptyMessageDelayed(GET_QUEUE_GIFT, 1000);//轮询队列获取礼物
}

PriorityBlockingQueue:基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,他也是一个无界的队列。add()并不进行排序操作,只有在取数据时才进行排序

public static PriorityBlockingQueue<User> queue = new PriorityBlockingQueue<User>();  
  
public static void main(String[] args) {  
    queue.add(new User(1,"wu"));  
    queue.add(new User(5,"wu5"));  
    queue.add(new User(23,"wu23"));  
    queue.add(new User(55,"wu55"));  
    queue.add(new User(9,"wu9"));  
    queue.add(new User(3,"wu3"));  
    for (User user : queue) {  
        try {  
            System.out.println(queue.take().name);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  
  
//静态内部类  
static class User implements Comparable<User>{  
  
    public User(int age,String name) {  
        this.age = age;  
        this.name = name;  
    }  
  
    int age;  
    String name;  
  
    @Override  
    public int compareTo(User o) {  
        return this.age > o.age ? -1 : 1;  
    }  
}  

DelayQueue:带有延迟时间的queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。

import java.util.concurrent.ArrayBlockingQueue;  
import java.util.concurrent.DelayQueue;  
import java.util.concurrent.Delayed;  
import java.util.concurrent.TimeUnit;  
  
  
public class DelayQueueTest {  
  
    private static DelayQueue delayQueue = new DelayQueue();  
  
    private static long count = 0L;  
  
    private static final int taskNum = 4;  
  
    public static void main(String[] args) throws InterruptedException {  
  
        Object num = new Object();  
  
        final DelayQueueTest delayQueueTest = new DelayQueueTest();  
        new Thread(new Runnable() {  
            public void run() {  
                try {  
                    delayQueueTest.producer();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
        while(true) {  
            if(delayQueue.size()==taskNum) {  
                break;  
            }  
        }  
        new Thread(new Runnable() {  
            public void run() {  
                try {  
                    delayQueueTest.consumer();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
  
  
        new Thread(new Runnable() {  
            public void run() {  
                try {  
                    delayQueueTest.count();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
    }  
  
    private void count() throws InterruptedException {  
        while(true) {  
            Thread.sleep(1000);  
            count++;  
            System.out.println("时间值="+count);  
            if(taskNum==count) {  
                break;  
            }  
        }  
    }  
  
    private void producer() throws InterruptedException {  
        for(int i=0; i<taskNum; i++) {  
            DelayedItem temp = new DelayedItem(i+"",i,(i+1));  
            System.out.println("生产者="+temp);  
            delayQueue.put(temp);  
        }  
    }  
  
    private void consumer() throws InterruptedException {  
        while(true) {  
            System.out.println("消费者="+delayQueue.take());  
            count = 0;  
        }  
    }  
  
  
    static class DelayedItem<T> implements  Delayed{  
        private String key;  
        private T item;  
        private long liveTime;  
        private long removeTime;  
  
        public DelayedItem(String key,T item,long liveTime) {  
            this.key = key;  
            this.item = item;  
            this.liveTime = liveTime;  
            this.removeTime = liveTime;  
        }  
  
        /** 
         * 当返回值小于等于0时则缓存时间到达,take将取出元素 
         * @param unit 
         * @return 
         */  
        public long getDelay(TimeUnit unit) {  
  
            return removeTime-count;  
        }  
  
        public int compareTo(Delayed o) {  
            if(o instanceof DelayedItem) {  
                //已经在队列中存在的对象  
                DelayedItem<T> tmpDelayedItem = (DelayedItem<T>)o;  
                //System.out.println("比较对象==="+tmpDelayedItem.key+"==="+this.key);  
                //失效时间越长的排到队尾  
                if(this.removeTime > tmpDelayedItem.removeTime) {  
                    return 1;  
                } else if(this.removeTime == tmpDelayedItem.removeTime) {  
                    return 0;  
                } else {  
                    return -1;  
                }  
            }  
            return -1;  
        }  
  
        @Override  
        public String toString() {  
            return "DelayedItem{" +  
                   "key='" + key + '\'' +  
                   ", item=" + item +  
                   ", liveTime=" + liveTime +  
                   ", removeTime=" + removeTime +  
                   '}';  
        }  
    }  
} 

运行结果:

生产者=DelayedItem{key='0', item=0, liveTime=1, removeTime=1}  
生产者=DelayedItem{key='1', item=1, liveTime=2, removeTime=2}  
生产者=DelayedItem{key='2', item=2, liveTime=3, removeTime=3}  
生产者=DelayedItem{key='3', item=3, liveTime=4, removeTime=4}  
时间值=1  
消费者=DelayedItem{key='0', item=0, liveTime=1, removeTime=1}  
时间值=1  
时间值=2  
消费者=DelayedItem{key='1', item=1, liveTime=2, removeTime=2}  
时间值=1  
时间值=2  
时间值=3  
消费者=DelayedItem{key='2', item=2, liveTime=3, removeTime=3}  
时间值=1  
时间值=2  
时间值=3  
时间值=4  
消费者=DelayedItem{key='3', item=3, liveTime=4, removeTime=4}  

SynchronousQueue:一种没有缓冲的队列,生产者产生的数据直接被消费者获取并消费。一个没有容量的并发队列有什么用了?或者说存在的意义是什么?SynchronousQueue 的实现非常复杂,SynchronousQueue 内部没有容量,但是由于一个插入操作总是对应一个移除操作,反过来同样需要满足。那么一个元素就不会再SynchronousQueue 里面长时间停留,一旦有了插入线程和移除线程,元素很快就从插入线程移交给移除线程。也就是说这更像是一种信道(管道),资源从一个方向快速传递到另一方 向。需要特别说明的是,尽管元素在SynchronousQueue 内部不会“停留”,但是并不意味之SynchronousQueue 内部没有队列。实际上SynchronousQueue 维护者线程队列,也就是插入线程或者移除线程在不同时存在的时候就会有线程队列。既然有队列,同样就有公平性和非公平性特性,公平性保证正在等待的插入线 程或者移除线程以FIFO的顺序传递资源。显然这是一种快速传递元素的方式,也就是说在这种情况下元素总是以最快的方式从插入着(生产者)传递给移除着(消费者),这在多任务队列中是最快处理任务的方式。在线程池的相关章节中还会更多的提到此特性。

它模拟的功能类似于生活中一手交钱一手交货这种情形,像那种货到付款或者先付款后发货模型不适合使用SynchronousQueue。首先要知道SynchronousQueue没有容纳元素的能力,即它的isEmpty()方法总是返回true,但是给人的感觉却像是只能容纳一个元素。

import java.util.Random;  
import java.util.concurrent.SynchronousQueue;  
import java.util.concurrent.TimeUnit;  
  
public class SynchronousQueueTest {  
    public static void main(String[] args) throws InterruptedException {  
        SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();  
      
        new Product(queue).start();  
        new Customer(queue).start();  
    }  
    static class Product extends Thread{  
        SynchronousQueue<Integer> queue;  
        public Product(SynchronousQueue<Integer> queue){  
            this.queue = queue;  
        }  
        @Override  
        public void run(){  
            while(true){  
                int rand = new Random().nextInt(1000);  
                System.out.println("生产了一个产品:"+rand);  
                System.out.println("等待三秒后运送出去...");  
                try {  
                    TimeUnit.SECONDS.sleep(3);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                try {  
                    queue.put(rand);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                  
                System.out.println(queue.isEmpty());  
            }  
        }  
    }  
    static class Customer extends Thread{  
        SynchronousQueue<Integer> queue;  
        public Customer(SynchronousQueue<Integer> queue){  
            this.queue = queue;  
        }  
        @Override  
        public void run(){  
            while(true){  
                try {  
                    System.out.println("消费了一个产品:"+queue.take());  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                System.out.println("------------------------------------------");  
            }  
        }  
    }  
}

运行结果:

生产了一个产品:542  
等待三秒后运送出去...  
true  
消费了一个产品:542  
生产了一个产品:183  
等待三秒后运送出去...  
------------------------------------------  
true  
消费了一个产品:183  
------------------------------------------  
生产了一个产品:583  
等待三秒后运送出去...  
true  
消费了一个产品:583  
------------------------------------------  
展开阅读全文

没有更多推荐了,返回首页