java BlockingQueue阻塞队列及condition解析

1、BlockingQueue使用

介绍:BlockingQueue即阻塞队列,常用的是用于实现生产者与消费者模式,它算是一种使用ReentrantLock特性的一个典型的案例

先上代码介绍下BlockingQueue的基本使用和场景

public class TestQueue {
    static BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(10);

    {
        init();
    }

    public void init() {
        new Thread(()->{
            while(true) {
                try {
                    System.out.println(blockingQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        TestQueue init = new TestQueue();
        new Thread(()->{
            while(true) {
                try {
                    blockingQueue.put("jack");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            while(true) {
                try {
                    blockingQueue.put("jack2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

如上main线程初始化TestQueue类调用了init方法创建了一个消费者线程阻塞在blockingQueue.take()方法处,创建生产者线程往阻塞队列放数据即可被消费者线程消费,上述代码即是一个简单的生产者消费者队列

2、BlockingQueue原理分析

我们先看下ArrayBlockingQueue的构造方法

public ArrayBlockingQueue(int var1, boolean var2) {
    this.itrs = null;
    if (var1 <= 0) {
        throw new IllegalArgumentException();
    } else {
    	// 创建了一个内部数组,大小为阻塞队列的大小值
        this.items = new Object[var1];
        // 创建了一个重入锁
        this.lock = new ReentrantLock(var2);
        // 根据锁创建了两个Condition用于后续条件判断
        this.notEmpty = this.lock.newCondition();
        this.notFull = this.lock.newCondition();
    }
}

然后我们看下put方法

public void put(E var1) throws InterruptedException {
    checkNotNull(var1);
    ReentrantLock var2 = this.lock;
    // 拿到锁加锁操作,并且做了一些额外的事
    var2.lockInterruptibly();
    try {
        while(this.count == this.items.length) {
        	// 当前长度等于数组最大长度时,
        	// 阻塞并挂起当前线程,等待消费者唤醒
            this.notFull.await();
        }
		// 加入到数组
        this.enqueue(var1);
    } finally {
        var2.unlock();
    }

}
private void enqueue(E var1) {
    Object[] var2 = this.items;
    // 获取到当前数组,并且拿到当前put的数组下标,设置当前值
    var2[this.putIndex] = var1;
    if (++this.putIndex == var2.length) {
    	// 如果当前值数组满了,那么设置putIndex 为0,即下一次put会从0开始
    	// (下次put时满了必定会阻塞,直到消费者把0位置的元素消费掉了,
    	// putIndex的设计即保证了按下标0开始一直循环,而且每次设置值是当前下标的值
    	// 必定是已经被消费掉的空位置)
        this.putIndex = 0;
    }

    ++this.count;
    // 唤醒因为队列为空阻塞在notEmpty的一个消费者
    this.notEmpty.signal();
}

再看下take()方法

public E take() throws InterruptedException {
	// 大致和put方法类似
    ReentrantLock var1 = this.lock;
    var1.lockInterruptibly();

    Object var2;
    try {
        while(this.count == 0) {
        	// 当目前数组为0时condition阻塞当前消费者等待被唤醒
            this.notEmpty.await();
        }

        var2 = this.dequeue();
    } finally {
        var1.unlock();
    }

    return var2;
}
  private E dequeue() {
        Object[] var1 = this.items;
        // takeIndex即消费者的下标,也是从0开始,即新进先出原则,设计和putIndex类似,
        // 依次循环,并且可以保证每次拿到的takeIndex下标的对象都是有值的
        // (没有值会阻塞在上面的this.notEmpty.await();)
        Object var2 = var1[this.takeIndex];
        var1[this.takeIndex] = null;
        if (++this.takeIndex == var1.length) {
            this.takeIndex = 0;
        }

        --this.count;
        if (this.itrs != null) {
            this.itrs.elementDequeued();
        }
		// 当消费了一个元素后,唤醒当数组满了之后阻塞的其中一个生产者
        this.notFull.signal();
        return var2;
    }

总结:ArrayBlockingQueue主要就是往数组加元素和取元素来实现功能,其中的有界队列使用了condition的await()阻塞和signal()唤醒来实现线程的等待(condition的功能类似于Object的wait/notify,主要用于等待和唤醒使用)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值