菜鸟看源码之LinkedBlockingQueue

首先扯点别的:已经不记得上次是什么时候做仰卧起坐的了,现在重新开始锻炼起来,腹肌还是得保持的。但是刚做了两天就感觉小腹酸疼。做20个都得咬牙坚持。明天继续做。

今天分析一下LinkedBlockingQueue的源码。LinkedBlockingQueue 是基于链表的阻塞队列,按照先进先出的顺序来排列元素。默认长度可以达到Integer.MAX_VALUE 。也可以指定LinkedBlockingQueue最大长度。添加的元素不能为null。

LinkedBlockingQueue的继承结构

在这里插入图片描述

节点类

static class Node <E> {
    E item;
    Node <E> next;
    Node(E x) {
        item = x;
    }
}

注意一下:头节点(下文一律称作head)的item为null。尾节点(下文一律称作last)的next为null。

相关成员变量:

    //容量上限,默认是Integer.MAX_VALUE
    private final int capacity;

    //元素的个数
    private final AtomicInteger count = new AtomicInteger();

     //头节点,head.item==null
    transient Node<E> head;

    //尾节点,last.next==null
    private transient Node<E> last;

	//如果要取出元素,要持有这个锁
    private final ReentrantLock takeLock = new ReentrantLock();

    //标记队列是否为空
    private final Condition notEmpty = takeLock.newCondition();

    //如果要添加元素,要持有这个锁
    private final ReentrantLock putLock = new ReentrantLock();

    //标记队列是否已满
    private final Condition notFull = putLock.newCondition();

构造方法

创建一个容量是Integer.MAX_VALUELinkedBlockingQueue

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

创建一个容量是capacityLinkedBlockingQueue

public LinkedBlockingQueue(int capacity) {
    if(capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node <E> (null);
}

创建一个容量是Integer.MAX_VALUELinkedBlockingQueue,并把集合c中的所有元素加入LinkedBlockingQueue

public LinkedBlockingQueue(Collection <? extends E> c) {
    //初始化容量为Integer.MAX_VALUE
    this(Integer.MAX_VALUE);
    final ReentrantLock putLock = this.putLock;
    putLock.lock(); // Never contended, but necessary for visibility
    try {
        int n = 0;
        for(E e: c) {
            if(e == null) throw new NullPointerException();
            if(n == capacity) throw new IllegalStateException("Queue full");
            enqueue(new Node <E> (e));
            ++n;
        }
        count.set(n);
    } finally {
        putLock.unlock();
    }
}

把元素添加到队列的末尾

private void enqueue(Node <E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node; //等价于 last.next = node;last =last.next;
}

在添加集合c中的元素到LinkedBlockingQueue 中的时候,如果集合c中存在元素为null,则抛出空指针异常,如果集合c中的元素个数超过了LinkedBlockingQueue的容量,抛出一个队列已满的异常。

相关方法

LinkedBlockingQueue 的 offer(E e) 方法

添加一个元素到队列尾部,如果队列满了,返回false,不会添加。

public boolean offer(E e) {
    if(e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    if(count.get() == capacity) return false;
    //这里将c置为-1,成功添加一个元素以后,
    int c = -1;
    Node <E> node = new Node <E> (e);
    final ReentrantLock putLock = this.putLock;
    //注释0处,先获取锁,
    putLock.lock();
    try {
        if(count.get() < capacity) {
            //队列未满,添加元素
            enqueue(node);
            c = count.getAndIncrement(); //添加完元素后count要加1。
            if(c + 1 < capacity) //如果添加元素以后,队列没有满
                notFull.signal(); //唤醒notFull上等待的线程,
        }
    } finally {
        //释放锁
        putLock.unlock();
    }
    //注释1处,表示此时队列不为空,唤醒notEmpty上等待的线程。
    if(c == 0) 
        signalNotEmpty();
    return c >= 0;
}

注释0处,先获取锁,获取了锁,再检查一次队列是否满了,没有满的话,就添加到队尾,返回true。

注释1处,表示此时队列不为空,唤醒notEmpty上等待的线程。这个方法只会在put或offer方法中被调用。

 private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}
LinkedBlockingQueue 的 boolean offer(E e, long timeout, TimeUnit unit)

在获取锁之后,最多等待 unit 的时间,如果添加成功,返回true,否则返回false。在获取锁的等待过程中是可以被中断的。被中断则抛出
InterruptedException。

 public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
    if(e == null) throw new NullPointerException();
    long nanos = unit.toNanos(timeout);
    int c = -1;
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        while(count.get() == capacity) {
            if(nanos <= 0) return false;
            nanos = notFull.awaitNanos(nanos);
        }
        enqueue(new Node <E> (e));
        c = count.getAndIncrement();
        if(c + 1 < capacity) notFull.signal();
    } finally {
        putLock.unlock();
    }
    if(c == 0) signalNotEmpty();
    return true;
}
LinkedBlockingQueue 的 void put(E e) 方法。

添加一个元素,如果队列已满的话,这个方法会阻塞,直到成功添加元素或者当前线程被中断。没有返回值。

public void put(E e) throws InterruptedException {
    if(e == null) throw new NullPointerException();
    int c = -1;
    Node <E> node = new Node <E> (e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    //使用lockInterruptibly方法,可以响应中断
    putLock.lockInterruptibly();
    try {
        //如果队列已满,就阻塞
        while(count.get() == capacity) {
            notFull.await();
        }
        //直到队列不满的时候,才添加元素
        enqueue(node);
        c = count.getAndIncrement();
        if(c + 1 < capacity) //如果队列未满,则唤醒notFull上等待的线程
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if(c == 0) //表示队列不为空,唤醒notEmpty上等待的线程
        signalNotEmpty();
}
LinkedBlockingQueue 的 take() 方法

从队列中获取并删除一个元素, 如果队列为空,则阻塞,直到获取元素,或者被中断。

 public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        //队列为空,则阻塞
        while(count.get() == 0) {
            notEmpty.await();
        }
        //直到队列不为空,才获取数据
        x = dequeue();
        c = count.getAndDecrement();
        if(c > 1) //队列不为空,唤醒notEmpty上等待的线程
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if(c == capacity) //队列未满,唤醒notFull上等待的线程
        signalNotFull();
    return x;
}

注意:take方法内部调用 dequeue 方法,会导致返回的元素从队列中删除。

LinkedBlockingQueue 的 dequeue() 方法
private E dequeue() {
    // assert takeLock.isHeldByCurrentThread();
    // assert head.item == null;
    Node <E> h = head;
    Node <E> first = h.next;
    h.next = h; // help GC
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}

将head.next 赋值给 head,相当于删除了一个节点。

唤醒notFull上等待的线程,这个方法只会在take或poll方法中 被调用

 private void signalNotFull() {
     final ReentrantLock putLock = this.putLock;
     putLock.lock();
     try {
         notFull.signal();
     } finally {
         putLock.unlock();
     }
 }
LinkedBlockingQueue 的 poll() 方法
public E poll() {
    final AtomicInteger count = this.count;
    if(count.get() == 0)
        return null;
    E x = null;
    int c = -1;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        if(count.get() > 0) {
            //如果队列不为空,调用dequeue获取并删除元素
            x = dequeue();
            c = count.getAndDecrement();
            if(c > 1) //获取元素以后,如果队列不为空,唤醒notEmpty上等待的线程
                notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    if(c == capacity) //获取元素以后,如果队列未满,唤醒notFull上等待的线程
        signalNotFull();
    return x;
}

跟 take 方法类似,只是这个方法如果获取不到元素会一直等待,锁不会被中断。

LinkedBlockingQueue 的 public E poll(long timeout, TimeUnit unit) 方法

获取锁以后,在指定时间内获取元素,直到时间耗尽返回null,或者正常返回元素,或者被中断,抛出 InterruptedException。

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    E x = null;
    int c = -1;
    long nanos = unit.toNanos(timeout);
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while(count.get() == 0) {
            if(nanos <= 0)
                return null;
            nanos = notEmpty.awaitNanos(nanos);
        }
        x = dequeue();
        c = count.getAndDecrement();
        if(c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if(c == capacity)
        signalNotFull();
    return x;
}
LinkedBlockingQueue 的 E peek() 方法

获取队列中第一个元素,不会删除元素。

public E peek() {
    if(count.get() == 0)
        return null;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        Node <E> first = head.next;
        if(first == null)
            return null;
        else
            return first.item;
    } finally {
        takeLock.unlock();
    }
}

参考链接:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值