• 阻塞队列与非阻塞队列


      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

  • 相关阅读:
    linux tar详解
    兼容ie和firefox的日期函数获取两个日期相差的天数
    把Response.Redirect放到TryCatch中出的错误
    如何把string类型转为enum类型

    string.Format的大括号处理
    全国默哀三分钟
    网页快捷键上下翻页(兼容firefox和ie)
    通过免费手机短信来控制电脑“执行者”介绍
    刚刚发现了一个好玩的关于有道的
  • 原文地址:https://www.cnblogs.com/gdpuzxs/p/6748299.html
Copyright © 2020-2023  润新知