Java阻塞队列

阻塞队列

阻塞队列顾名思义,首先它是一个队列,而一个阻塞队列在数据结构中所起作用大致如下图所示:

 

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
  • 当阻塞队列满时,往队列里添加元素的操作将会被阻塞

试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。

同样

试图往已满的阻塞队列中添加元素的线程同样也会被阻塞,直到其他的线程从列表中移除一个或者多个元素或者完全清空阻塞队列后使队列重新变得空闲起来并后续新增。

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要线程阻塞,什么时候需要唤醒线程,因为这一切BlockingQueue都给包办了

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

BlockingQueue的核心方法

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法

1)支持阻塞的插入方法:意思是当队列满时队列会阻塞插入元素的线程,直到队列不满

2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

在阻塞队列不可用时,这两个附加操作提供了4种处理方式

抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException("Queue full") 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。

返回特殊值:插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null

一直阻塞:当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。

超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永远不会被阻塞,而且使用offer方法时,该方法永远返回true

JDK7 提供了 7 个阻塞队列。分别是

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

LinkedBlockingQueue :一个由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

DelayQueue:一个使用优先级队列实现的无界阻塞队列。

SynchronousQueue:一个不存储元素的阻塞队列。

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列

ArrayBlockingQueue 

ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序

demo

传统的多线程模式

class ShareData //资源类
{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void increment() throws Exception {

        lock.lock();
        try {
            //1.先判断
            while (number != 0) {
                //等待,不能生产
                condition.await();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            //3.通知唤醒机制
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

public class ProdConsumer_TraditionDemo {
    /*
    题目:一个初始值为零的变量,两个线程对其交替操作,一个加一,一个减一,来五轮
    1.线程    操作(方法)  资源类
    2.判断    干活          通知
    3.防止虚假唤醒机制
     */
    public static void main(String[] args) {
        ShareData shareData = new ShareData();

        new Thread(() -> {
            for (int i = 1; i < 6; i++) {
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "AA").start();

        new Thread(() -> {
            for (int i = 1; i < 6; i++) {
                try {
                    shareData.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }, "BB").start();
    }
}

  

 

 

 

 

 






posted on 2020-05-17 10:08  fyccaishao  阅读(161)  评论(0编辑  收藏  举报

导航