iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 > 线程池02-LinkedBlockingQueue 阻塞队列
  • 835
分享到

线程池02-LinkedBlockingQueue 阻塞队列

摘要

首先,我们先了解一下什么是阻塞队列: 当队列满了时,队列会阻塞插入元素的线程,直到队列不满; 当队列为空时,获取元素的线程会等待队列变成非空。 常用到的方法 上面是对阻塞队列的简单了解,下面重点分析一下LinkedBlocki


	线程池02-LinkedBlockingQueue 阻塞队列
[数据库教程]

首先,我们先了解一下什么是阻塞队列:

  • 当队列满了时,队列会阻塞插入元素的线程,直到队列不满;

  • 当队列为空时,获取元素的线程会等待队列变成非空。

常用到的方法

技术图片

上面是对阻塞队列的简单了解,下面重点分析一下LinkedBlockingQueue。

源码分析

node节点

  • 可以看出是单向的链表结构
static class Node {
    E item;
    Node next;
    Node(E x) { item = x; }
}

构造方法和参数

  • 如果未设置初始容量,则默认是Integer.MAX_VALUE;
   
   private final int capacity;

    
    private final AtomicInteger count = new AtomicInteger();

    
    transient Node head;

    
    private transient Node 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(null);//初始化的时候设置头节点和尾节点为两个空节点
    }

插入

put 方法

  • 如果队列已经满了,则放入到条件队列中。
public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        Node node = new Node(e);//创建新节点
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();//获取put
        try {
            //判断存入的元素个数和配置的数量是否相等,如果相等。那么将当前线程放入到条件队列中
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);//将节点插入到末尾
            c = count.getAndIncrement();//元素数量+1
            if (c + 1 < capacity)//当前元素数量小于容量的时候,唤醒“存入条件队列”的头节点到同步队列
                notFull.signal();
        } finally {
            putLock.unlock();释放put锁
        }
        // 唤醒获取条件队列的头节点
        if (c == 0)
            signalNotEmpty();
    }

//将节点设置为尾节点
private void enqueue(Node node) {
    last = last.next = node;
}
// 唤醒“获取条件队列”中的首节点
private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();//t获取ake锁
        try {
            notEmpty.signal();//唤醒“获取条件队列”中的首节点
        } finally {
            takeLock.unlock();
        }
    }

offer 方法

  • 如果超过容量就无法插入
  public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)//如果超过容量直接返回false,表示不能再插入数据
            return false;
        int c = -1;
        Node node = new Node(e);//新建节点
        final ReentrantLock putLock = this.putLock;
        putLock.lock();//获取put锁
        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;
    }

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();//数量减1
            if (c > 1)//如果元素书大于1,则调用“获取条件队列”中的元素放入同步队列
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        // 唤醒存入条件队列的头节点
        if (c == capacity)
            signalNotFull();
        return x;//返回头节点
    }

// 获取头节点元素
private E dequeue() {
        Node h = head;
        Node first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }


// 唤醒“存入条件队列”的头节点到同步队列
 private void signalNotFull() {
      final ReentrantLock putLock = this.putLock;
      putLock.lock();
      try {
          notFull.signal();
      } finally {
          putLock.unlock();
      }
  }

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) {//当前容器数量大于0时
                x = dequeue();
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        // 唤醒存入条件队列的头节点
        if (c == capacity)
            signalNotFull();
        return x;
    }

总结

1.如何保证当队列没有消息或者消息满了的时候,进行监听?

上面看代码的时候,两段代码刚开始是有点懵的。

1.存入的方法
  // 唤醒获取条件队列的头节点
  if (c == 0) signalNotEmpty();

2.获取的方法
  // 唤醒存入条件队列的头节点
  if (c == capacity) signalNotFull();

其实这就监听的重要环节。
逻辑是这样的。以存入为例:
1.如果当前节点为0,说明队列中没有任务;
2.唤醒“获取条件队列”的头节点,去尝试获取元素。如果获取到则执行,如果没有,则依然放入到“获取条件队列”的末尾;
3.这样就可以保证在存入数据的时候,实时监听获取节点元素了。

线程池02-LinkedBlockingQueue 阻塞队列

原文地址:https://www.cnblogs.com/perferect/p/13723857.html

您可能感兴趣的文档:

--结束END--

本文标题: 线程池02-LinkedBlockingQueue 阻塞队列

本文链接: https://www.lsjlt.com/news/7896.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • oracle怎么查询当前用户所有的表
    要查询当前用户拥有的所有表,可以使用以下 sql 命令:select * from user_tables; 如何查询当前用户拥有的所有表 要查询当前用户拥有的所有表,可以使...
    99+
    2024-05-14
    oracle
  • oracle怎么备份表中数据
    oracle 表数据备份的方法包括:导出数据 (exp):将表数据导出到外部文件。导入数据 (imp):将导出文件中的数据导入表中。用户管理的备份 (umr):允许用户控制备份和恢复过程...
    99+
    2024-05-14
    oracle
  • oracle怎么做到数据实时备份
    oracle 实时备份通过持续保持数据库和事务日志的副本来实现数据保护,提供快速恢复。实现机制主要包括归档重做日志和 asm 卷管理系统。它最小化数据丢失、加快恢复时间、消除手动备份任务...
    99+
    2024-05-14
    oracle 数据丢失
  • oracle怎么查询所有的表空间
    要查询 oracle 中的所有表空间,可以使用 sql 语句 "select tablespace_name from dba_tablespaces",其中 dba_tabl...
    99+
    2024-05-14
    oracle
  • oracle怎么创建新用户并赋予权限设置
    答案:要创建 oracle 新用户,请执行以下步骤:以具有 create user 权限的用户身份登录;在 sql*plus 窗口中输入 create user identified ...
    99+
    2024-05-14
    oracle
  • oracle怎么建立新用户
    在 oracle 数据库中创建用户的方法:使用 sql*plus 连接数据库;使用 create user 语法创建新用户;根据用户需要授予权限;注销并重新登录以使更改生效。 如何在 ...
    99+
    2024-05-14
    oracle
  • oracle怎么创建新用户并赋予权限密码
    本教程详细介绍了如何使用 oracle 创建一个新用户并授予其权限:创建新用户并设置密码。授予对特定表的读写权限。授予创建序列的权限。根据需要授予其他权限。 如何使用 Oracle 创...
    99+
    2024-05-14
    oracle
  • oracle怎么查询时间段内的数据记录表
    在 oracle 数据库中查询指定时间段内的数据记录表,可以使用 between 操作符,用于比较日期或时间的范围。语法:select * from table_name wh...
    99+
    2024-05-14
    oracle
  • oracle怎么查看表的分区
    问题:如何查看 oracle 表的分区?步骤:查询数据字典视图 all_tab_partitions,指定表名。结果显示分区名称、上边界值和下边界值。 如何查看 Oracle 表的分区...
    99+
    2024-05-14
    oracle
  • oracle怎么导入dump文件
    要导入 dump 文件,请先停止 oracle 服务,然后使用 impdp 命令。步骤包括:停止 oracle 数据库服务。导航到 oracle 数据泵工具目录。使用 impdp 命令导...
    99+
    2024-05-14
    oracle
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作