JUC并发编程LinkedBlockingQueue队列源码分析

发布时间:2023-05-08 16:26:01 作者:iii
来源:亿速云 阅读:167

JUC并发编程LinkedBlockingQueue队列源码分析

1. 引言

在多线程编程中,队列是一种常用的数据结构,用于在生产者和消费者之间传递数据。Java并发包(JUC)提供了多种线程安全的队列实现,其中LinkedBlockingQueue是一个基于链表实现的有界或无界阻塞队列。本文将深入分析LinkedBlockingQueue的源码,探讨其内部实现机制、线程安全性以及性能特点。

2. LinkedBlockingQueue概述

LinkedBlockingQueue是一个基于链表的阻塞队列,它可以在队列满时阻塞生产者线程,在队列空时阻塞消费者线程。LinkedBlockingQueue既可以是有界的,也可以是无界的。如果构造时指定了容量,则队列是有界的;否则,队列的容量为Integer.MAX_VALUE,即无界队列。

2.1 主要特性

2.2 适用场景

LinkedBlockingQueue适用于以下场景:

3. 源码分析

3.1 类结构

LinkedBlockingQueue的类结构如下:

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    // 内部节点类
    static class Node<E> {
        E item;
        Node<E> next;
        Node(E x) { item = x; }
    }

    // 队列容量
    private final int capacity;

    // 当前队列中的元素数量
    private final AtomicInteger count = new AtomicInteger();

    // 队列头节点
    transient Node<E> head;

    // 队列尾节点
    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();

    // 构造方法
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

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

    // 其他方法...
}

3.2 内部节点类

LinkedBlockingQueue内部使用Node类来表示链表中的节点。每个节点包含一个元素item和一个指向下一个节点的引用next

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

3.3 队列容量与元素数量

LinkedBlockingQueue通过capacity字段表示队列的容量,通过count字段表示当前队列中的元素数量。count是一个AtomicInteger类型的变量,用于保证线程安全。

private final int capacity;
private final AtomicInteger count = new AtomicInteger();

3.4 队列头尾节点

LinkedBlockingQueue使用headlast两个字段分别表示队列的头节点和尾节点。头节点是一个哨兵节点,不存储实际元素。

transient Node<E> head;
private transient Node<E> last;

3.5 锁与条件队列

LinkedBlockingQueue使用两个锁来保证线程安全:takeLock用于消费者线程,putLock用于生产者线程。每个锁都有一个对应的条件队列:notEmpty用于消费者线程等待队列非空,notFull用于生产者线程等待队列非满。

private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();

3.6 构造方法

LinkedBlockingQueue提供了两个构造方法:一个无参构造方法,创建一个无界队列;另一个构造方法接受一个容量参数,创建一个有界队列。

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

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

3.7 入队操作

LinkedBlockingQueue提供了多种入队操作,包括putofferadd。其中,put方法在队列满时会阻塞,直到队列有空闲空间;offer方法在队列满时会立即返回falseadd方法在队列满时会抛出IllegalStateException

3.7.1 put方法

put方法用于将元素插入队列尾部。如果队列已满,则当前线程会被阻塞,直到队列有空闲空间。

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;
    putLock.lockInterruptibly();
    try {
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

3.7.2 offer方法

offer方法用于将元素插入队列尾部。如果队列已满,则立即返回false

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    if (count.get() == capacity)
        return false;
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() < capacity) {
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}

3.7.3 add方法

add方法用于将元素插入队列尾部。如果队列已满,则抛出IllegalStateException

public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

3.8 出队操作

LinkedBlockingQueue提供了多种出队操作,包括takepollremove。其中,take方法在队列空时会阻塞,直到队列有元素;poll方法在队列空时会立即返回nullremove方法在队列空时会抛出NoSuchElementException

3.8.1 take方法

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.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

3.8.2 poll方法

poll方法用于从队列头部取出元素。如果队列为空,则立即返回null

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) {
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

3.8.3 remove方法

remove方法用于从队列头部取出元素。如果队列为空,则抛出NoSuchElementException

public E remove() {
    E x = poll();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}

3.9 其他方法

LinkedBlockingQueue还提供了其他一些方法,如sizeremainingCapacitycontainsclear等。这些方法的实现相对简单,这里不再赘述。

4. 线程安全性分析

LinkedBlockingQueue通过分离锁机制(takeLockputLock)来保证线程安全。生产者线程和消费者线程可以并发操作队列,而不会相互阻塞。只有在队列满或空时,才会阻塞相应的线程。

4.1 生产者线程安全

生产者线程通过putLock来保证线程安全。当队列满时,生产者线程会被阻塞,直到队列有空闲空间。

4.2 消费者线程安全

消费者线程通过takeLock来保证线程安全。当队列空时,消费者线程会被阻塞,直到队列有元素。

4.3 条件队列

LinkedBlockingQueue使用条件队列notFullnotEmpty来管理阻塞的线程。当队列满时,生产者线程会被放入notFull条件队列中;当队列空时,消费者线程会被放入notEmpty条件队列中。

5. 性能分析

LinkedBlockingQueue的性能主要取决于以下几个方面:

6. 总结

LinkedBlockingQueue是一个高效、线程安全的阻塞队列实现,适用于生产者-消费者模型、任务调度和消息传递等场景。通过分离锁机制和条件队列,LinkedBlockingQueue能够有效地减少锁竞争,提高并发性能。在实际应用中,开发者可以根据具体需求选择合适的队列容量,以平衡内存使用和性能。

通过对LinkedBlockingQueue源码的深入分析,我们可以更好地理解其内部实现机制,从而在实际开发中更加灵活地使用这一强大的并发工具。

推荐阅读:
  1. Java进阶(9) - 并发(JUC)
  2. java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

juc linkedblockingqueue

上一篇:Vue3中怎么实现选取头像并裁剪

下一篇:window系统nodejs安装opencv环境配置的方法是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》