Java阻塞队列(BlockingQueue)

目录

什么是阻塞队列

阻塞队列支持的方法

阻塞队列的实现原理

1.Condition

2.LockSupport

Java中的阻塞队列


什么是阻塞队列

阻塞队列(BlockingQueue)是一个附加支持阻塞的插入和移除的队列。

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

  2. 支持阻塞的移除方法:当队列为空时,队列会阻塞插入元素的线程,知道队列变为非空。

阻塞队列支持的方法

方法/处理方式抛出异常返回特殊值一直阻塞超时退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
检查方法element()peek()不可用不可用

注:

  • 抛出异常:当队列满时,如果再往队列中插入元素,会抛出IllegalStateExeception("Queue full")异常,当队列空时,从队列里获取元素会抛出NoSuchElementException异常。

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

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

  • 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。当队列空时,如果消费者线程从队列里移除元素,队列会阻塞消费者线程一段时间,如果超过了指定的时间,消费者线程就会退出。

阻塞队列的实现原理

阻塞队列的实现方式的是通知者模式。通知者模式是一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

不同的阻塞队列实现方式不同,以ArrayBlockingQueue为例,剖析阻塞队列的实现。ArrayBlockingQueue使用Condition实现:

public class ArrayBlockingQueue<E> {

    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;

    public ArrayBlockingQueue(int capacity, boolean fair) {
        //初始化lock和Condition
        this.lock = new ReentrantLock(fair);
        this.notEmpty = this.lock.newCondition();
        this.notFull = this.lock.newCondition();
    }

    public void put(E e) throws InterruptedException {
        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while(this.count == this.items.length) {
                //如果队列满,等待
                this.notFull.await();
            }
            this.enqueue(e);
        } finally {
            lock.unlock();
        }
    }


    public E take() throws InterruptedException {
        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        Object var2;
        try {
            while(this.count == 0) {
                //如果队列为空,等待
                this.notEmpty.await();
            }
            var2 = this.dequeue();
        } finally {
            lock.unlock();
        }
        return var2;
    }

    private void enqueue(E e) {
        //插入元素,通知等待的消费者
        this.notEmpty.signal();
    }

    private E dequeue() {
        //移除元素,通知等待的生茶者
        this.notFull.signal();
        return e;
    }
}

1.Condition

通知者模式的应用是在Condition的实现上,Condition维护了一个FIFO(First Input First Output)等待队列,队列的每个节点都包含了一个线程的引用。Condition是由Lock创建的,Lock可以有多个Condition对象,所以Lock有一个同步队列和多个等待队列。Object监视器模型(Monitor),一个对象只有一个同步队列和一个等待队列。

Conditon拥有首尾节点的引用,当线程调用Condition.await()方法,将会阻塞线程,以当前线程构造节点(同步器的静态内部类AbstractQueuedSynchronizer.Node),并将节点从尾部加入等待队列。当调用Condition.singal()方法,会将Condition等待队列中的首节点移到Lock()同步队列中,然后唤醒线程。当调用Condition.singalAll()方法,相当于对等待队列的每个节点做一次singal()方法。

public class ConditionObject implements Condition {
    private transient AbstractQueuedSynchronizer.Node firstWaiter;
    private transient AbstractQueuedSynchronizer.Node lastWaiter;

    public final void signal() {
        AbstractQueuedSynchronizer.Node first = this.firstWaiter;
        if (first != null) {
            //唤醒同步队列首节点,调用LockSupport.unpark()
            this.doSignal(first);
        }
    }

    public final void signalAll() {
        AbstractQueuedSynchronizer.Node first = this.firstWaiter;
        if (first != null) {
            //唤醒同步队列全部节点,调用LockSupport.unpark()
            this.doSignalAll(first);
        }
    }

    public final void await() throws InterruptedException {
        //当前线程加入等待队列
        AbstractQueuedSynchronizer.Node node = this.addConditionWaiter();
        //释放锁
        int savedState = AbstractQueuedSynchronizer.this.fullyRelease(node);
        int interruptMode = 0;

        while (!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) {
            //阻塞当前线程
            LockSupport.park(this);
            if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
                break;
            }
        }
    }
}

2.LockSupport

LockSuopport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。通过调用Unsafe的虚拟机方法来完成阻塞和唤醒操作。

在jdk1.6中,LockSupport增加了park(Object blocker)、parkNanos(Obejcet blocker, long nanos)、parkUtil(Object blocker, long deadline)。其中参数blocker是用来标识当前线程在等待的对象,该对象主要用于问题排查和系统监控。

方法名称描述
void park()阻塞当前线程,如果调用unpark(Thread thread)方法或者当前线程被中断,才能从park()方法返回。
void parkNanos(long nanos)阻塞当前线程,最长不超过nanos秒,超时返回。
void parkUntil(long deadline)阻塞当前线程,直到deadline时间(从1970年开始到deadline时间毫秒数)
void unpark(Thread thread)唤醒处于阻塞状态的线程thread。
/**
 * LockSupport类
**/
public class LockSupport {
    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long PARKBLOCKER;
    private static final long SECONDARY;
    private static final long TID;

    private LockSupport() {
    }

    private static void setBlocker(Thread t, Object arg) {
        U.putObject(t, PARKBLOCKER, arg);
    }

    public static void unpark(Thread thread) {
        if (thread != null) {
            U.unpark(thread);
        }

    }

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        U.park(false, 0L);
        setBlocker(t, (Object)null);
    }

    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0L) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            U.park(false, nanos);
            setBlocker(t, (Object)null);
        }

    }

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        U.park(true, deadline);
        setBlocker(t, (Object)null);
    }

    public static Object getBlocker(Thread t) {
        if (t == null) {
            throw new NullPointerException();
        } else {
            return U.getObjectVolatile(t, PARKBLOCKER);
        }
    }

    public static void park() {
        U.park(false, 0L);
    }

    public static void parkNanos(long nanos) {
        if (nanos > 0L) {
            U.park(false, nanos);
        }

    }

    public static void parkUntil(long deadline) {
        U.park(true, deadline);
    }

    static final int nextSecondarySeed() {
        Thread t = Thread.currentThread();
        int r;
        if ((r = U.getInt(t, SECONDARY)) != 0) {
            r ^= r << 13;
            r ^= r >>> 17;
            r ^= r << 5;
        } else if ((r = ThreadLocalRandom.current().nextInt()) == 0) {
            r = 1;
        }

        U.putInt(t, SECONDARY, r);
        return r;
    }

    static final long getThreadId(Thread thread) {
        return U.getLong(thread, TID);
    }

    static {
        PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker");
        SECONDARY = U.objectFieldOffset(Thread.class, "threadLocalRandomSecondarySeed");
        TID = U.objectFieldOffset(Thread.class, "tid");
    }
}

Java中的阻塞队列

  1. ArrayBlockingQueue:一个用数组实现的有界阻塞队列。按照FIFO(First Input First Output)原则对元素进行排序,默认非公平。
  2. LinkedBlockingQueue:一个用链表实现的有界阻塞队列,此队列的默认和最大长度为Integer.MAX_VALUE。按照FIFO的原则对元素进行排序。
  3. PriorityBlockingQueue:一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序升序排列,可以实现Comparator接口来进行排序。
  4. DelayQueue:一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue实现,队列中元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素,只有延迟期满时才能从队列中提取元素。
  5. SynchronousQueue:一个不存储元素的阻塞队列,每一个put操作必须等待一个take操作,否则不能添加元素。
  6. LinkedTransferQueue:一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。transfer方法,如果当前有消费者正在等待接收元素(消费者使用take()或带时间显示的poll()方法时),transfet方法可以把生产者传入的元素立刻transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
  7. LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。多了XXFirst和XXLast方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值