【多线程】阻塞线程| 一图看懂ArrayBlockingQueue源码

简介: 是一个数组实现的环形队列,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作

ArrayBlockingQueue简介

是一个数组实现的环形队列,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作

ArrayBlockingQueue队列的原理

利用了Lock锁的Condition通知机制进行阻塞控制。

核心:一把锁,两个条件

//数据元素数组
final Object[] items;
//下一个待取出元素索引
int takeIndex;
//下一个待添加元素索引
int putIndex;
//元素个数
int count;
//内部锁
final ReentrantLock lock;
//消费者
private final Condition notEmpty;
//生产者
private final Condition notFull;  
public ArrayBlockingQueue(int capacity, boolean fair) {
    ...
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

一图看懂put与take源码

网络异常,图片无法展示
|

1.1 put总结

public void put(E e) throws InterruptedException {
  //检查是否为空
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        //获取自选锁
        lock.lockInterruptibly();
        try {
        //阻塞队列已满,则将生产者挂起,等待消费者唤醒
            while (count == items.length)
                notFull.await();
            // 进入阻塞队列
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        //将值放入队列
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        //唤醒生产者
        notEmpty.signal();
    }

我们在对流程图进行文字的总结

  1. 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
  2. 判断阻塞队列是否满了,如果满了,则调用await方法阻塞这个线程,notFull(生产者)挂起,最后释放lock锁,等待被消费者线程唤醒。
  3. 如果没有满,则调用enqueue方法将元素put进阻塞队列。
  4. 唤醒一个标记为notEmpty(消费者)的线程。

1.2 take总结

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        //自选获取锁
        lock.lockInterruptibly();
        try {
        //如果队列为空,则消费者挂起
            while (count == 0)
                notEmpty.await();
              //获取队列值
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        //获取值
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        //唤醒生产者
        notFull.signal();
        return x;
    }

总结

我们在对流程图进行文字的总结

  1. 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
  2. 判断阻塞队列是否为空,如果是空,则调用await方法阻塞这个线程,notEmpty(消费者)挂起,最后释放lock锁,等待被生产者线程唤醒。
  3. 如果没有空,则调用dequeue方法。
  4. 唤醒一个标记为notFull(生产者)的线程
相关文章
|
15天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
26天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
28天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
1月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
1月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法
|
1月前
|
Java C#
C#学习系列相关之多线程(五)----线程池ThreadPool用法
C#学习系列相关之多线程(五)----线程池ThreadPool用法
|
1月前
|
存储 安全 Java
深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践
线程使程序能够通过同时执行多个任务而更有效地运行。 线程可用于在不中断主程序的情况下在后台执行复杂的任务。 创建线程 有两种创建线程的方式。 扩展Thread类 可以通过扩展Thread类并覆盖其run()方法来创建线程:
107 1
深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践
|
1月前
|
数据采集 存储 Java
「多线程大杀器」Python并发编程利器:ThreadPoolExecutor,让你一次性轻松开启多个线程,秒杀大量任务!
「多线程大杀器」Python并发编程利器:ThreadPoolExecutor,让你一次性轻松开启多个线程,秒杀大量任务!
|
30天前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
67 0
|
1天前
|
监控
写一个线程来监控各线程是否发生阻塞
写一个线程来监控各线程是否发生阻塞
7 0

热门文章

最新文章