concurrent 笔记 - 有界阻塞队列实现

阻塞队列的实现

1. 有界队列

队列中的元素个数有限, 队列为空时, get 操作或阻塞, 队列满时, add 操作会阻塞。
常见的有界队列时 ArrayBlockingQueueLinkedBlockingQueue.
有界队列的写法, 一般是有一把全局大锁同步读写操作,notFull 和 notEmpty 两个 Condition 作为条件谓词进行空满检验。 ArrayBlockingQueueLinkedBlockingQueue也是这样实现的, 不同的是前者只有一把大锁, 读写没有分离; 后者有2把锁,效率会高一些

2. 无界队列

队列中元素个数不限制,因此不会产生 get, put 的阻塞操作. 常见的无界非阻塞队列为 ConcurrentLinkedQueue
ConcurrentLinkedQueue 因为是无解的, 所以 offer 操作永远返回 true, take 操作如果没有元素返回 null。 ConcurrentLinkedQueue 的增删节点是使用 cas 操作指针进行的, 满足框架

class ConcurrentLinkedQueue {
    Unsafe unsafe =  ... 
    Node heda, tail;
    
    // 插入
    boolean offer(Node newNode) {
        for (;;) {
            Node tail = this.tail;  // 先获取一下 tail 
            // 看 tail 是否发生了变化, CAS 更新 tail 引用
            if(unsafe.cas(tailoffset, tail, newNode)) {   
                tail.next = newNode;   // 连上 next 引用
                break;
            }  
                
        }
    }
    
    Node take() {
       // ... 
    }
    
    static {
       headOffset = unsafe.getOffset(this.head) ...  // 得到属性偏移量
       tailOffset = unsafe.getOffset(this.tail) ...  // 得到属性偏移量
    }

} 

此外, ConcurrentLinkedQueue 并不是安全的遍历, 其中并没有一个 AtomicInteger 的 size 属性, 要获取 size 就是遍历一下, 而因为是无锁遍历, 所以即使发生节点增删, 也不能感知到, 因此只能获取一个近似值

3. jdk 有界队列分析
```java
class LinkedBlockingQueue<E>{
    private final int capacity;  // 队列容量
    // 当前队列内元素个数 AtomicInteger
    private final AtomicInteger count = new AtomicInteger();

    Node<E> head,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();
    
    /** 读锁下: 条件谓词进行通知 */
    private void signalNotEmpty() {
        this.takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            this.takeLock.unlock();
        }
    }

    /** 写锁下: 条件谓词进行通知 */
    private void signalNotFull() {
        this.putLock.lock();
        try {
            notFull.signal();
        } finally {
            this.putLock.unlock();
        }
    }
    
    /** 写操作 */
    public void put(E e){
        int c = -1;
        Node<E> node = new Node<E>(e);
        this.putLock.lock();
        try {
            // 条件的 while 检测, 为了防止被错误唤醒
            while (this.count.get() == capacity) {  // count 已满检验
                notFull.await();  // notFull 条件谓词等待
            }
            enqueue(node);  // 入队操作
            int c = this.count.getAndIncrement();  // count + 1
            if (c + 1 < capacity)
            // 因为是 signal, 而不是 signalAll, 所以要通知其它同类 put 操作 notFull
                notFull.signal();   
        } finally {
            this.putLock.unlock();
        }
        // 最重要:put 后获取 take 锁通知 notEmpty
        if (c == 0)
            signalNotEmpty();
    }
    
    /** 读操作 */
    public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        this.takeLock.lockInterruptibly(); // 上锁
        try {
            while (this.count.get() == 0) { // siz = 0的检验
                notEmpty.await();  // 非空条件谓词等待
            }
            x = dequeue();
            c = this.count.getAndDecrement();  // count 增加
            if (c > 1)
                notEmpty.signal(); // 通知其它 take 操作 notEmpty 
        } finally {
            this.takeLock.unlock();
        }
        // 最重要:take 后获取 put 锁通知 notFull
        if (c == capacity)
            signalNotFull();   
        return x;
    }
    
    // 无锁下的出队逻辑
    private E dequeue() {
        Node<E> h = head, first = h.next;
        h.next = h; // help GC next指向自己
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值