java多线程-阻塞队列

1.基本思路

      之前接触的队列都是非阻塞队列,不如LinkedList(实现了Dequeue接口)、PriorityQueue。使用非阻塞队列有一个很大的问题就是,它不会对当前线程产生阻塞。那么在面对类似生产者-消费者问题时,就必须额外实现同步策略以及线程间唤醒策略,这个实现起来非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞。比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞,直到阻塞队列中有了元素。当队列中有了元素之后,被阻塞的线程会自动被唤醒(不需要自己写代码)。这样就提供了极大的方便。


2.阻塞队列类型

    (1)ArrayBlockingQueue

            基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须指定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的线程能够最优先访问队列

    (2)LinkedBlockingQueue

            基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE

    (3)PriorityBlockingQueue

            上面两种都是先进先出队列,而PriorityBlockingQueue不是,他会按照元素的优先级对元素进行排序,按照优先级顺序出队列,每次出队列都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限。前面两种都是有界队列。

    (4)DelayQueue

              基于PriorityQueue,是一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到元素。DelayQueue也是一个无界队列,因此往此队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞


3.阻塞队列中方法vs非阻塞队列中方法

(1)非阻塞队列中方法

          1)add(E e):元素添加到末尾,如果添加成功返回true;如果插入失败(队列已满),则抛出异常

          2)remove():移除队首元素,如果移除成功返回true;如果移除失败(队列空),则抛出异常

          3)offer(E e):元素添加到末尾,如果添加成功返回true;如果插入失败(队列已满),则返回false

          4)poll():移除队首元素,如果移除成功返回true;如果移除失败(队列空),则false

          5)peek():获取队首元素,如果成功返回队首元素;否则返回null

     对于非阻塞队列,一般情况下建议使用offer、poll、peek方法,不建议使用add和remove方法。因为使用offer、poll、peek三个方法可以通过返回值判断操作成功与否。注意:非阻塞队列中的方法都没有同步措施


(2)阻塞队列中方法

         除了上面的5个方法,还提供了另外4个非常有用的方法:

        1)put(E e):向队尾插入元素,如果队列满,则等待

        2)take():从队首取元素,如果队列为空,则等待

        3)offer(E  e,long  timeout,TimeUnit unit):向队尾存入元素,如果队列满则等待timeout的时间,如果到达时间期限,还没有插入成功则返回false,否则返回true

        4)poll(long  timeout,TimeUnit unit):从队首取元素,如果队列为空则等待tmeout的时间,如果到达时间期限,还没有取到成功则返回null,否则返回队首元素


4.阻塞队列实现原理

        以ArrayBlockingQueue为例,看下其中的成员变量

    private final E[] items;                      //用来存储元素

    private int takeIndex;                        //队首下标

    private int putIndex;                         //队尾下标

    private int count;                            //元素个数

    private final ReentrantLock lock;

    private final Condition notEmpty;              //等待条件,不为空

    private final Condition notFull;
         构造方法:

    public ArrayBlockingQueue(int capacity, boolean fair) 
    {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = (E[]) new Object[capacity];             //申请一个数组
        lock = new ReentrantLock(fair);                      //实际上是指定公平还是非公平锁
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
          put方法:

    public void put(E e) throws InterruptedException 
    {
        if (e == null) throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try 
        {
            try 
            {
                while (count == items.length)
                    notFull.await();
            } 
            catch (InterruptedException ie) 
            {
                notFull.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            insert(e);
        }
        finally 
        {
            lock.unlock();
        }
    }

       再看一下其中的insert方法:

    private void insert(E x) 
    {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }
      那么就可以看出,实际上就是一个生产者-消费者的模型。也就是先使用lock加锁,再判断队列是否满,满的话则等待,然后将数据插入队列,最后则唤醒消费者线程

       take()方法

   public E take() throws InterruptedException 
  {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try 
        {
            while (count == 0)
                notEmpty.await();
            return extract();
        } 
        finally 
        {
            lock.unlock();
        }
    }
        其中的extract()方法:

    private E extract() 
   {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();
        return x;
    }
     实际上就是一个消费者的操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值