阻塞队列与非阻塞队列

  java提供的线程安全的Queue可以分为阻塞队列与非阻塞队列。其中阻塞队列的典型代表就是LinkedBlockingQueue与ArrayBlockingQueue,非阻塞队列的代表就是ConcurrentLinkedQueue。下面对阻塞个队列进行总结一下。

  LinkedBlockingQueue创建的时候可以指定队列大小,也可以不指定,不指定默认是Integer.MAX_VALUE。看源码如下:

 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

  LinkedBlockingQueue添加元素方法有add(),offer(),put()。下面谈谈这三个方法的区别,add()方法如下:

public static void main(String[]args){
        
        LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2);
        
        queue.add("A");
        
        queue.add("B");
        
        queue.add("C");
        
    }

  输出结果如下,也就是add方法当元素的个数超出了队列的大小,会抛出异常。

  

   offer()方法如下:

public static void main(String[]args){
        
        LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2);
        Boolean q1=queue.offer("A");
        Boolean q2=queue.offer("B");
        Boolean q3=queue.offer("C");
        System.out.println(queue.toString());
        System.out.println(q1);
        System.out.println(q2);
        System.out.println(q3);
    }

  输出结果如下:也就是说offer()方法当元素的个数超出队列大小的时候,不会抛出异常,而是添加失败,返回false。

  

  put()方法如下:

public static void main(String[]args) throws InterruptedException{
        
        LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2);
        queue.put("A");
        queue.put("B");
        queue.put("C");
        System.out.println("队列进入阻塞状态");
    }

  输出结果如下:队列进入阻塞状态,直到队列中有元素被消费,元素C才能添加到队列中。

  

  同样,LinkedBlockingQueue移除元素也有三个方法,区别如下:

  poll()方法:当队列为空的时候,返回null。

  remove()方法:当队列为空的时候,抛出异常。

  take()方法:当队列为空的时候,进入阻塞状态。

  注意peek()方法,虽然也是取出头元素,但是不删除元素。

  接下看一下ArrayBlockingQueue,同样也是阻塞队列,但是队列是有界队列,也就是说创建ArrayBlockingQueue队列的时候,必须指定队列的大小。源码如下:capacity指定队列的大小,fair指定锁是公平锁还是非公平锁。

 public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    /**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and the specified access policy.
     *
     * @param capacity the capacity of this queue
     * @param fair if {@code true} then queue accesses for threads blocked
     *        on insertion or removal, are processed in FIFO order;
     *        if {@code false} the access order is unspecified.
     * @throws IllegalArgumentException if {@code capacity < 1}
     */
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

  同样,添加元素,移触元素的方法与LinkedBlockingQueue的一样。

  接下来总结一下这两个阻塞队列的不同之处:

  (1)队列中锁的实现不同

    ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁。

    

         LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock。

    

     所以在多线程情况下,由于LinkedBlockingQueue的读写锁分离,所以效率会高于ArrayBlockingQueue。

   (2)队列大小初始化方式不同

        ArrayBlockingQueue实现的队列中必须指定队列的大小。

          LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是Integer.MAX_VALUE。

   (3)在生产或消费时操作不同  

      ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的。

           LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node<E>进行插入或移除,会影响性能。

    对队列进行如下测试:分别将1000000元素推入到两个队列中,分别计算一下耗时时间。

public static void main(String[]args) throws InterruptedException{
        
        ArrayBlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(1000000);
        long start = System.currentTimeMillis();
        for(int i=0;i<1000000;i++){
            queue.add(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("ArrayBlockingQueue进行10000次插入操作耗时:"+(end-start)+"ms");
        LinkedBlockingQueue<Integer> queue1=new LinkedBlockingQueue<Integer>(1000000);
        start = System.currentTimeMillis();
        for(int i=0;i<1000000;i++){
            queue1.add(i);
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedBlockingQueue进行10000次插入操作耗时:"+(end-start)+"ms");
    }

    输出结果:LinkedBlockingQueue的性能比ArrayBlockingQueue要差很多。

    

    参考网址:http://super-robin.iteye.com/blog/1997423

原文地址:https://www.cnblogs.com/gdpuzxs/p/6748299.html