数据结构和算法之【阻塞队列】下篇

目录

前言

接口优化

定义抽象类

实现类一:使用单锁来实现

实现类二:使用两把锁来实现


前言

上篇文章中,用单锁实现了一个阻塞队列,可以思考下还有哪些地方可以完善和优化的点,这里提出一个可以完善的点和一个可以性能优化的点

  • 可以完善的地方:新增方法,指定等待时间,如果超过指定时间就不再继续阻塞等待
  • 可以性能优化的地方:单锁实现的方式,存放元素和取出元素的方法用的是同一把锁,当存放元素时不能取出元素,这样就有一定的性能损耗,可以使用两把锁来实现存放和取出元素相互不影响,提升程序性能

接口优化

接口中,新增两个方法

  • 入队时,如果队列已满,最多等待指定时间后不再等待
  • 出队时,如果队列已空,最多等待指定时间后不再等待

阻塞队列接口的完整定义如下:

java 复制代码
package algorithm.blocking.queue;

import java.util.concurrent.TimeUnit;

/**
 * 阻塞队列接口
 */
public interface BlockingQueue {
    /**
     * 入队:当队列满时,会一直阻塞等待到队列不满为止
     */
    void put(Object e) throws InterruptedException;

    /**
     * 入队:可以指定最大等待时间
     */
    boolean offer(Object e, long timeout, TimeUnit timeUnit) throws InterruptedException;

    /**
     * 出队:当队列空时,会一直阻塞等待到队列不空为止
     */
    Object take() throws InterruptedException;

    /**
     * 出队:可以指定最大等待时间
     */
    Object poll(long timeout, TimeUnit timeUnit) throws InterruptedException;

    /**
     * 队列中元素个数
     */
    int size();
}

定义抽象类

java 复制代码
package algorithm.blocking.queue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 定义基于双向链表实现阻塞队列的抽象类
 */
public abstract class AbstractLinkedListBlockingQueue implements BlockingQueue {
    // 虚拟头节点
    protected Node dummyHead;
    // 虚拟尾节点
    protected Node dummyTail;
    // 元素个数
    protected AtomicInteger size = new AtomicInteger();
    // 默认最大容量
    public static final int DEFAULT_MAX_CAPACITY = Integer.MAX_VALUE - 8;
    // 最大容量
    protected int maxCapacity;

    @Override
    public void put(Object e) throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        doEnter(e);
    }

    protected abstract void doEnter(Object e) throws InterruptedException;


    @Override
    public boolean offer(Object e, long timeout, TimeUnit timeUnit) throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        if (timeout <= 0) {
            throw new IllegalArgumentException();
        }
        if (timeUnit == null) {
            timeUnit = TimeUnit.MILLISECONDS;
        }
        return doEnter(e, timeout, timeUnit);
    }

    protected abstract boolean doEnter(Object e, long timeout, TimeUnit timeUnit) throws InterruptedException;

    @Override
    public Object take() throws InterruptedException {
        Node node = doOut();
        return node == null ? null : node.val;
    }

    protected abstract Node doOut() throws InterruptedException;

    @Override
    public Object poll(long timeout, TimeUnit timeUnit) throws InterruptedException {
        if (timeout <= 0) {
            throw new IllegalArgumentException();
        }
        if (timeUnit == null) {
            timeUnit = TimeUnit.MILLISECONDS;
        }
        Node node = doOut(timeout, timeUnit);
        return node == null ? null : node.val;
    }

    protected abstract Node doOut(long timeout, TimeUnit timeUnit) throws InterruptedException;

    @Override
    public int size() {
        return size.get();
    }

    // 初始化虚拟头尾节点
    protected void initDummyHeadTailNode() {
        dummyHead = new Node();
        dummyTail = new Node();
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
    }

    // 尾部添加节点
    protected void addLast(Object e) {
        Node addNode = new Node(e);
        Node prev = dummyTail.prev;
        dummyTail.prev = addNode;
        addNode.next = dummyTail;
        addNode.prev = prev;
        prev.next = addNode;
    }

    // 头部移除节点
    protected Node removeFirst() {
        Node removeNode = dummyHead.next;
        Node next = dummyHead.next.next;
        next.prev = dummyHead;
        dummyHead.next = next;
        removeNode.prev = null;
        removeNode.next = null;
        return removeNode;
    }


    // 数据节点
    protected static class Node {
        Object val;
        Node prev;
        Node next;

        public Node() {
        }

        public Node(Object val) {
            this.val = val;
        }

        public Node(Object val, Node prev, Node next) {
            this.val = val;
            this.prev = prev;
            this.next = next;
        }
    }
}

实现类一:使用单锁来实现

java 复制代码
package algorithm.blocking.queue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 基于链表和单锁方式实现的阻塞队列
 */
public class SingleLockLinkedListBlockingQueue extends AbstractLinkedListBlockingQueue {
    // 定义一把锁
    private final Lock lock = new ReentrantLock();
    private final Condition putWaits = lock.newCondition();
    private final Condition takeWaits = lock.newCondition();

    public SingleLockLinkedListBlockingQueue() {
        this.maxCapacity = DEFAULT_MAX_CAPACITY;
        initDummyHeadTailNode();
    }

    // 实例化时指定最大容量
    public SingleLockLinkedListBlockingQueue(int maxCapacity) {
        this.maxCapacity = maxCapacity;
        initDummyHeadTailNode();
    }

    @Override
    protected void doEnter(Object e) throws InterruptedException {
        lock.lock();
        try {
            while (size.get() == maxCapacity) {
                // 队列已满,线程阻塞等待在putWaits
                putWaits.await();
            }
            addLast(e);
            size.getAndIncrement();
            // 唤醒阻塞在takeWaits上的等待线程
            takeWaits.signalAll();
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean doEnter(Object e, long timeout, TimeUnit timeUnit) throws InterruptedException {
        lock.lock();
        try {
            timeout = timeUnit.toNanos(timeout);
            while (size.get() == maxCapacity) {
                if (timeout <= 0) {
                    return false;
                }
                timeout = putWaits.awaitNanos(timeout);
            }
            addLast(e);
            size.getAndIncrement();
            takeWaits.signalAll();
            return true;
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected Node doOut() throws InterruptedException {
        lock.lock();
        try {
            while (size.get() == 0) {
                // 队列中没有元素,线程阻塞等待在takeWaits
                takeWaits.await();
            }
            Node outNode = removeFirst();
            size.getAndDecrement();
            // 唤醒阻塞在putWaits上的等待线程
            putWaits.signalAll();
            return outNode;
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected Node doOut(long timeout, TimeUnit timeUnit) throws InterruptedException {
        lock.lock();
        try {
            timeout = timeUnit.toNanos(timeout);
            while (size.get() == 0) {
                if (timeout <= 0) {
                    return null;
                }
                timeout = takeWaits.awaitNanos(timeout);
            }
            Node outNode = removeFirst();
            size.getAndDecrement();
            putWaits.signalAll();
            return outNode;
        } finally {
            lock.unlock();
        }
    }
}

实现类二:使用两把锁来实现

java 复制代码
package algorithm.blocking.queue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 基于链表和双锁方式实现的阻塞队列
 */
public class DoubleLockLinkedListBlockingQueue extends AbstractLinkedListBlockingQueue {
    // 定义双锁
    private final Lock putLock = new ReentrantLock();
    private final Condition putWaits = putLock.newCondition();

    private final Lock takeLock = new ReentrantLock();
    private final Condition takeWaits = takeLock.newCondition();

    public DoubleLockLinkedListBlockingQueue() {
        this.maxCapacity = DEFAULT_MAX_CAPACITY;
        initDummyHeadTailNode();
    }

    public DoubleLockLinkedListBlockingQueue(int maxCapacity) {
        this.maxCapacity = maxCapacity;
        initDummyHeadTailNode();
    }

    @Override
    protected void doEnter(Object e) throws InterruptedException {
        int count;
        putLock.lock();
        try {
            while (size.get() == maxCapacity) {
                // 队列已满,阻塞当前线程
                putWaits.await();
            }
            // 入队
            addLast(e);
            count = size.getAndIncrement();
            if (count < maxCapacity - 1) {
                // 极联唤醒
                putWaits.signal();
            }
        } finally {
            putLock.unlock();
        }
        // 处于临界条件下,唤醒阻塞等待在takeWaits的线程
        // 此处临界条件为:队列中的元素从无到有
        if (count == 0) {
            takeSignal();
        }
    }

    private void takeSignal() {
        takeLock.lock();
        try {
            takeWaits.signal();
        } finally {
            takeLock.unlock();
        }
    }

    @Override
    protected boolean doEnter(Object e, long timeout, TimeUnit timeUnit) throws InterruptedException {
        int count;
        putLock.lock();
        try {
            timeout = timeUnit.toNanos(timeout);
            while (size.get() == maxCapacity) {
                if (timeout <= 0) {
                    return false;
                }
                timeout = putWaits.awaitNanos(timeout);
            }
            addLast(e);
            count = size.getAndIncrement();
            if (count < maxCapacity - 1) {
                putWaits.signal();
            }
        } finally {
            putLock.unlock();
        }
        if (count == 0) {
            takeSignal();
        }
        return true;
    }

    @Override
    protected Node doOut() throws InterruptedException {
        Node outNode;
        int count;
        takeLock.lock();
        try {
            while (size.get() == 0) {
                // 队列为空,当前线程阻塞等待在takeWaits
                takeWaits.await();
            }
            // 出队
            outNode = removeFirst();
            count = size.getAndDecrement();
            if (count > 1) {
                // 极联唤醒
                takeWaits.signal();
            }
        } finally {
            takeLock.unlock();
        }
        // 临界条件:队列从满到不满
        if (count == maxCapacity) {
            putSignal();
        }
        return outNode;
    }

    private void putSignal() {
        putLock.lock();
        try {
            putWaits.signal();
        } finally {
            putLock.unlock();
        }
    }

    @Override
    protected Node doOut(long timeout, TimeUnit timeUnit) throws InterruptedException {
        Node outNode;
        int count;
        takeLock.lock();
        try {
            timeout = timeUnit.toNanos(timeout);
            while (size.get() == 0) {
                if (timeout <= 0) {
                    return null;
                }
                timeout = takeWaits.awaitNanos(timeout);
            }
            outNode = removeFirst();
            count = size.getAndDecrement();
            if (count > 1) {
                takeWaits.signal();
            }
        } finally {
            takeLock.unlock();
        }
        if (count == maxCapacity) {
            putSignal();
        }
        return outNode;
    }
}
相关推荐
小蒜学长2 分钟前
基于过程性评价的ai助学平台(代码+数据库+LW)
java·spring boot·后端·微信小程序·过程性评价
洁洁!6 分钟前
手搓 C++ 线程池详细讲解以及如何面试应答
java·c++·面试
萝卜白菜。8 分钟前
TongWeb生产环境配置规范之三:优化TongWeb启动速度
java·开发语言
信誓旦旦的程序猿18 分钟前
【Python量化实战宝典 #20】动量、反转择时手册:趋势跟随与均值回归(Python 可跑)
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
电商全栈程序猿23 分钟前
2026 开源商城系统选型指南:开发者实测对比Tigshop、ShopXO、CRMEB
java·运维·开源
骑着蜗牛撵大象32731 分钟前
用 Codex 批量做数字人商品口播视频:从分镜 JSON 到成片校验的流水线
java·ffmpeg·json·音视频·数字人·视频·codex
博风32 分钟前
使用Spring官网下载Springboot骨架
java·spring boot·spring
金銀銅鐵1 小时前
[Java] 用 CLI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
qq_436962181 小时前
金蝶云星空数据分析怎么做?
java·数据库·数据分析
代码山河1 小时前
键盘输入与输出:Scanner与System.out详解
java·学习·架构·教程·面向对象·项目