Java并发编程之阻塞队列(BlockingQueue)详解

目录
  • 队列
  • 阻塞队列
  • ArrayBlockingQueue
    • 重要属性
    • 构造方法
    • 添加元素
      • add(e)
      • offer(e)
      • put(e)
      • offer(e,time,unit)
    • 移除元素
      • take()
      • dequeue()
  • LinkedBlockingQueue
    • 重要属性
    • 构造方法
    • 添加元素
      • offer(e)
      • put(e)
    • 移除元素
      • poll()
      • take()
  • 对比
  • 总结

大家好,我是小黑,一个在互联网苟且偷生的农民工。

队列

学过数据结构的同学应该都知道,队列是数据结构中一种特殊的线性表结构,和平时使用的List,Set这些数据结构相比有点特殊,它的特殊之处在于它只允许在队列的头部(Head)进行删除操作,在尾部(Tail)进行插入操作,这种方式的队列我们称之为先进先出队列(FIFO)。

在JDK1.5中推出了队列这一数据结构的具体实现,接口Queue是对于队列的定义,并有一些列具有特殊功能的队列实现。

在Queue接口中定义了队列的如下方法:

其中add(E)并非Queue接口新定义,而是从Collection接口继承而来的。

阻塞队列

BlockingQueue接口也是在JDK1.5中推出,存放在java.util.concurrent包中,继承自Queue,所以在BlockingQueue中有Queue的所有方法。

从名字就可以看出BlockingQueue是一种阻塞队列,它支持在检索元素时如果队列为空可以一直阻塞等待直到有元素可以获取,同样在添加元素时如果队列已满会阻塞等待队列中有空闲的存储空间。

BlockingQueue的方法可以归纳为四类:

  • 在操作时如不能立即满足,会直接抛出异常
  • 在操作时如不能立即满足,则返回特殊的值,如插入、移除方法会返回false,检查方法会返回null
  • 在操作时如不能立即满足,则会阻塞等待,直到操作成功
  • 在操作时如不能立即满足,则会阻塞等待给定的时间长度,时间到达后如果还不能满足则返回null

这四类方法总结如下。

因为在BlockingQueue的一些方法中,会通过null表示某种操作的失败,所以不允许在BlockingQueue中存放null值元素,会在操作时抛出NullPointerExection异常。

BlockingQueue因为是一个容器嘛,所以它也有容量的限制,在具体实现类中有可以设置容量的实现类,也有不可以设置容量的实现类,不能设置容量的实现类容量默认为Integer.MAX_VALUE。

BlockingQueue是定义在java.util.concurrent包中,那么它在并发情况下到底是不是线程安全的呢?

在JDK提供的BlockingQueue的具体实现类中,上面表格中的方法实现都是线程安全的,在内部都使用了锁或者其他形式的并发控制保证操作的原子性。

但是有一点要注意,就是一些批量处理的方法例如addAll、containsAll、retainAll和removeAll这些方法并不一定是线程安全的,使用时注意。

说完BlockingQueue接口我们接下来看看它都有哪些具体的实现呢?以及在它们内部是如何做到线程安全和阻塞的呢?

ArrayBlockingQueue

ArrayBlockingQueue是一个底层由数组支持额有界阻塞队列。

重要属性

先来看看ArrayBlockingQueue中都有哪些属性。

// 存放元素的数组
final Object[] items;
// 用来记录取元素的下标,用于下一次在take,poll,remove,peek方法中使用
int takeIndex;
// 用来记录添加元素的下标,用于下一次put,offer,add等方法使用
int putIndex;
// 记录队列中元素数量
int count;
// 用于控制并发访问时保证线程安全的锁
final ReentrantLock lock;
// 用于队列空时阻塞和唤醒等待线程的条件
private final Condition notEmpty;
// 用于队列满时阻塞和唤醒等待线程的条件
private final Condition notFull;

我们通过这些队列中的属性基本可以知道ArrayBlockingQueue中都有哪些重要信息,可以看出ArrayBlockingQueue就是使用Object[]来存放元素的。

那么应该如何创建一个ArrayBlockingQueue呢?

构造方法

public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}

默认的构造方法需要传入一个int类型的capacity表示该队列的容量。在该构造方法中会调用另一个构造方法,传入一个默认值false。

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

从这个方法我们看出传入的false表示会在内部用于创建一个ReentrantLock对象,我们都知道ReentrantLock支持公平和非公平的实现,我们猜想一下,这里的这个fair值是不是表示该阻塞队列对于阻塞排队的线程支持公平和非公平的策略呢?这里先卖个关子,在后面的方法中我们具体说。

除了这两种创建的方式,ArrayBlockingQueue还支持传入一个Collection集合。

public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
    // 先创建一个ArrayBlockingQueue实例
    this(capacity, fair);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        int i = 0;
        try {
            // 循环将collection中的元素放入queue中
            for (E e : c) {
                checkNotNull(e);
                items[i++] = e;
            }
        } catch (ArrayIndexOutOfBoundsException ex) {
            // 如果collection的元素个数超出queue的容量大小,会抛出异常
            throw new IllegalArgumentException();
        }
        count = i;
        putIndex = (i == capacity) ? 0 : i;
    } finally {
        lock.unlock();
    }
}

添加元素

先来看看添加一个新元素到ArrayBlockingQueue是如何实现的,怎样保证线程安全的。

add(e)

public boolean add(E e) {
    // 调用父类中的add(e)方法
    return super.add(e);
}
public boolean add(E e) {
    // 这里会直接调用offer(e)方法,如果offer方法返回false,则直接抛出异常
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

add方法的实现逻辑本质上是对offer方法套了一层壳,如果offer方法返回false时,抛出异常。所以我们直接看offer方法的实现就好。

offer(e)

public boolean offer(E e) {
    // 这里先判断空,如果e为空会抛出空指针异常
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
	// 加锁,保证入队操作的原子性
    lock.lock();
    try {
        // 队列满时直接返回false
        if (count == items.length)
            return false;
        else {
            // 元素入队
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

可以看到offer方法的逻辑还是比较简单的,先检查入参不能为空,然后加锁保证入队操作的原子性,在获取锁成功后入队,如果队列已满则直接返回false,所以offer方法并不会阻塞。

put(e)

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();
    }
}

put方法和offer方法唯一的区别,就是会在队列满的时候使用Condition条件对象notFull阻塞等待。

private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    // 入队成功,唤醒等待的移除元素操作线程
    notEmpty.signal();
}

在enqueue方法中才会完成对队列中的数组元素的赋值动作,完成之后唤醒阻塞等待的移除元素操作线程。

offer(e,time,unit)

public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
    checkNotNull(e);
    // 加锁之前先获取需要等待的时间值
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length) {
            // 时间小于等于0时,返回false
            if (nanos <= 0)
                return false;
            // 阻塞等待指定时间
            nanos = notFull.awaitNanos(nanos);
        }
        enqueue(e);
        return true;
    } finally {
        lock.unlock();
    }
}

offer(e,time,unit)方法与offer(e)方法相比,主要时多了一个等待时间,会在时间到达时如果没有空间添加元素返回false。

移除元素

ArrayBlockingQueue中移除元素的方法主要有remove(),poll(),take(),poll(time,unit)四个。这几个方法的实现逻辑都比较简单,这里不在单独贴代码 。我们来看一下阻塞方法take()的实现即可。

take()

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
	// 加锁
    lock.lockInterruptibly();
    try {
        while (count == 0)
            // 如果元素数量==0,表示队列中为空,则阻塞等待
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

dequeue()

private E dequeue() {
    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;
}

LinkedBlockingQueue

LinkedBlockingQueue是一个基于链表结构的阻塞队列,可以在创建时指定边界大小,也可以不指定,在不指定边界时容量为Integer.MAX_VALUE。

重要属性

我们先来看看在LinkedBlockingQueue中都有哪些重要的属性。

// 内部类Node节点,用来存放链表中的元素
static class Node<E> {
    // 节点元素
	E item;
	// 当前节点的下一个节点,如果为空表示没有下一个节点
	Node<E> next;
	Node(E x) { item = x; }
}
// 队列的容量
private final int capacity;
// 队列中元素的数量
private final AtomicInteger count = new AtomicInteger();
// 头节点
transient Node<E> head;
// 最后一个节点
private transient Node<E> last;
// 获取元素时控制线程安全的锁
private final ReentrantLock takeLock = new ReentrantLock();
// 添加元素时控制线程安全的锁
private final ReentrantLock putLock = new ReentrantLock();
// 控制消费者的条件
private final Condition notEmpty = takeLock.newCondition();
// 控制生产者的条件
private final Condition notFull = putLock.newCondition();

在LinkedBlockingQueue中使用Node来存放元素,和指向下一个节点的链表指针。

构造方法

在LinkedBlockingQueue的构造方法中,会创建一个创建一个不存放元素的Node对象赋值给head和last。

public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    // 创建一个不存放元素的Node对象赋值给head和last
    last = head = new Node<E>(null);
}
public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                // 入队
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }

添加元素

offer(e)

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    if (count.get() == capacity)
        return false;
    int c = -1;
    Node<E> node = new Node<E>(e);
    // 使用putLock加锁
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() < capacity) {
            // 入队
            enqueue(node);
            // 数量+1
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                // 唤醒一个生产者线程
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        // 唤醒消费者线程
        signalNotEmpty();
    // 入队失败情况会返回false
    return c >= 0;
}

对于链表结构的LinkedBlockingQueue来说,入队操作要简单很多,只需要将node节点挂在最后一个节点last的next,然后将自己赋值给last。

private void enqueue(Node<E> node) {
    last = last.next = node;
}

put(e)

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    int c = -1;
    Node<E> node = new Node<E>(e);
    // 使用putLock加锁
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        while (count.get() == capacity) {
            // 如果队列容量已使用完则阻塞
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

对比结果也和我们最开始的方法汇总表格一样,offer(e)方法会在入队时如果队列已满直接返回false,而put(e)会一直阻塞等待,知道入队成功。

add(e)方法和offer(e,time,unit)方法实现逻辑上没有特殊之处,这里不再放源码。

移除元素

poll()

public E poll() {
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            return null;
        E x = null;
        int c = -1;
    // 使用takeLock加锁
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            if (count.get() > 0) {
                x = dequeue();
                c = count.getAndDecrement();
                if (c > 1)
                    // 还有元素时唤醒一个生产者线程
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            // 唤醒生产者线程
            signalNotFull();
        return x;
    }

poll()方法会在元素出队时如果没有元素则直接返回null。

// 出队方法
private E dequeue() {
    Node<E> h = head;
    Node<E> first = h.next;
    h.next = h;
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}

take()

public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    // 使用takeLock加锁
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            //阻塞等待
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            // 还有元素时唤醒一个消费者线程
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        // 唤醒生产者线程
        signalNotFull();
    return x;
}

同样,take方法会在没有元素时一直等待。

对比

我们来对比一下ArrayBlockingQueue和LinkedBlockingQueue都有哪些区别。

  • ArrayBlockingQueue基于数组实现,LinkedBlockingQueue基于链表实现
  • ArrayBlockingQueue在添加和移除元素的操作中共用一把锁,LinkedBlockingQueue使用takeLock和putLock两把锁
  • ArrayBlockingQueue在添加和移除元素时直接使用元素的类型处理,LinkedBlockingQueue需要转成Node对象
  • ArrayBlockingQueue创建时必须指定容量,LinkedBlockingQueue可以不指定,默认容量为Integer.MAX_VALUE

由于LinkedBlockingQueue使用两把锁将入队操作和出队操作分离,这会大大提高队列的吞吐量,在高并发情况下生产者和消费者可以并行处理,提高并发性能。

但是LinkedBlockingQueue默认是无界队列,要小心内存溢出风险,所以最好在创建时指定容量大小。

BlockingQueue接口的实现类除了本期介绍的这两种,还有PriorityBlockingQueue,SynchronousQueue,LinkedBlockingDeque等,每一个都有它独特的特性和使用场景,后面我们再单独深入解析。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java timezone设置和mybatis连接数据库时区设置方式

    目录 Java timezone设置和mybatis连接数据库时区设置 JVM时区设置 Mybatis Mybatis timezone问题 解决方法 Java timezone设置和mybatis连接数据库时区设置 JVM时区设置 springboot工程运行时,需要指定时区,这样获取的时间才会和系统时间相同.以下介绍方法: 1.查看当前时区 centos7以前(不含centos7) cat /etc/sysconfig/clock centos7以后(包含centos7) timedatec

  • Java SpringBoot快速集成SpringBootAdmin管控台监控服务详解

    目录 1.初识SpringBootAdmin 2.搭建服务端--POM文件中添加相关依赖 3.修改服务端application启动类 4.配置security安全信息 5.启动server服务端 6.搭建client客户端 总结 SpringBootAdmin是一个针对 Spring Boot 的 Actuator 接口进行 UI 美化封装的监控工具,它可以在列表中浏览所有被监控 spring-boot 项目的基本信息.详细的 Health 信息.内存信息.JVM 信息.垃圾回收信息.各种配置信

  • Java synchronized最细讲解

    目录 前言 Synchronization实现原理 先理解Java对象头与Monitor 1.对象头:锁的类型和状态和对象头的Mark Word息息相关: jdk6 之后做了改进,引入了偏向锁和轻量级锁: 1.无锁到偏向锁转化的过程 2.偏向锁升级轻量级 3.轻量级到重量级 总结 前言 线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据. 因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在

  • Java SpringBoot开发小技巧详解

    目录 一.SpringBoot开发小技巧 1.1 Lombok 1.2 dev-tools 1.3 Spring Initializr 总结 一.SpringBoot开发小技巧 1.1 Lombok 作用:在程序编译的时候,自动帮我们生成setter和getter方法以及我们的toString方法和我们的全参和无参构造器等等. 那么,怎么用呢?很简单,用下边这四个注解就行了: 1.@Data:自动生成setter和getter方法. 2.@ToString:自动生成toString方法. 3.@

  • Java SpringBoot+vue+实战项目详解

    目录 1.<锋迷商城>业务流程设计-接⼝规范 1.1 前后端分离与单体架构流程实现的区别 1.1.1单体架构 1.1.2 前后端分离架构 1.2 接口介绍 1.2.1接口概念 1.2.2接口规范 1.3 Swagger 1.3.1作用 1.3.2 Swagger整合 1.3.3 Swagger注解说明 1.3.4 Swagger-ui 插件 1.4 RESTful 总结 1.<锋迷商城>业务流程设计-接⼝规范 在企业项⽬开发中,当完成项⽬的需求分析.功能分析.数据库分析与设计之后,

  • 有关Java中的BeanInfo介绍

    目录 1.JavaBean介绍 2.JavaBean的自省 3.JavaBean内省工具Introspector 4.JavaBean内省结果BeanInfo 5.内省结果BeanInfo的类型 6.Spring的BeanUtils.copyProperties 7.BeanUtils并发问题优化 8.BeanUtils Setter属性识别优化 9.BeanUtils 性能测试 1.JavaBean介绍 维基百科JavaBean的定义:JavaBeans是Java中一种特殊的类,可以将多个对象

  • Java并发编程之阻塞队列(BlockingQueue)详解

    目录 队列 阻塞队列 ArrayBlockingQueue 重要属性 构造方法 添加元素 add(e) offer(e) put(e) offer(e,time,unit) 移除元素 take() dequeue() LinkedBlockingQueue 重要属性 构造方法 添加元素 offer(e) put(e) 移除元素 poll() take() 对比 总结 大家好,我是小黑,一个在互联网苟且偷生的农民工. 队列 学过数据结构的同学应该都知道,队列是数据结构中一种特殊的线性表结构,和平时

  • Java并发编程之阻塞队列深入详解

    目录 1. 什么是阻塞队列 2. 阻塞队列的代码使用 3. 生产者消费者模型 (1)应用一:解耦合 (2)应用二:削峰填谷 (3)相关代码 4.阻塞队列和生产者消费者模型功能的实现 1. 什么是阻塞队列 阻塞队列是一种特殊的队列,和数据结构中普通的队列一样,也遵守先进先出的原则同时,阻塞队列是一种能保证线程安全的数据结构,并且具有以下两种特性:当队列满的时候,继续向队列中插入元素就会让队列阻塞,直到有其他线程从队列中取走元素:当队列为空的时候,继续出队列也会让队列阻塞,直到有其他线程往队列中插入

  • Java 阻塞队列BlockingQueue详解

    目录 一. 前言 二. 认识BlockingQueue 三.BlockingQueue的核心方法: 四.常见BlockingQueue 五. 小结 一. 前言 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景. 二. 认识BlockingQueue 阻塞队列,

  • Java阻塞队列BlockingQueue详解

    目录 队列的类型 数据结构 阻塞队列 BlockingQueue 常见的阻塞队列 BlockingQueue API ArrayBlockingQueue 源码简解 生产者消费者模式 延迟队列 DelayQueue 队列的类型 无限队列(unbounded queue) 无容量限定,只随存储变化 有限队列(bounded queue) 定义了最大容量 向无限队列添加元素的所有操作都将永远不会阻塞(也是线程安全的),因此它可以增长到非常大的容量. 使用无限阻塞队列 BlockingQueue 设计

  • java 中 阻塞队列BlockingQueue详解及实例

    java 中 阻塞队列BlockingQueue详解及实例 BlockingQueue很好的解决了多线程中数据的传输,首先BlockingQueue是一个接口,它大致有四个实现类,这是一个很特殊的队列,如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒.同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作.

  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解 前言: 使用JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作.有时候,我们启动N个线程去做一件事情,只有当这N个线程都达到某一个临界点的时候,我们才能继续下面的工作,就是说如果这N个线程中的某一个线程先到达预先定义好的临界点,它必须等待其他N-1线程也到达这个临界点,接下来的工作才能继续,只要这N个线程中有1个线程没有到达所谓的临界点,其他线程就算抢先到达了临界点,也只能等待,只有所有这N

  • java并发编程专题(五)----详解(JUC)ReentrantLock

    上一节我们了解了Lock接口的一些简单的说明,知道Lock锁的常用形式,那么这节我们正式开始进入JUC锁(java.util.concurrent包下的锁,简称JUC锁).下面我们来看一下Lock最常用的实现类ReentrantLock. 1.ReentrantLock简介 由单词意思我们可以知道这是可重入的意思.那么可重入对于锁而言到底意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放.这模仿了 sy

  • java并发编程专题(三)----详解线程的同步

    有兴趣的朋友可以回顾一下前两篇 java并发编程专题(一)----线程基础知识 java并发编程专题(二)----如何创建并运行java线程 在现实开发中,我们或多或少的都经历过这样的情景:某一个变量被多个用户并发式的访问并修改,如何保证该变量在并发过程中对每一个用户的正确性呢?今天我们来聊聊线程同步的概念. 一般来说,程序并行化是为了获得更高的执行效率,但前提是,高效率不能以牺牲正确性为代价.如果程序并行化后, 连基本的执行结果的正确性都无法保证, 那么并行程序本身也就没有任何意义了.因此,

  • java并发编程StampedLock高性能读写锁详解

    目录 一.读写锁 二.悲观读锁 三.乐观读 一.读写锁 在我的<java并发编程>上一篇文章中为大家介绍了<ReentrantLock读写锁>,ReentrantReadWriteLock可以保证最多同时有一个线程在写数据,或者可以同时有多个线程读数据,但读写不能同时进行. 比如你正在做的是日志,有一个线程正在做写操作,但是在写日志的时候你可能需要把日志集中转移到集中管理日志服务,但是此时读线程不能读数据(因为无法获取读锁).面对这个需求,ReentrantReadWriteLoc

  • JAVA并发编程有界缓存的实现详解

    JAVA并发编程有界缓存的实现 1.有界缓存的基类 package cn.xf.cp.ch14; /** * *功能:有界缓存实现基类 *时间:下午2:20:00 *文件:BaseBoundedBuffer.java *@author Administrator * * @param <V> */ public class BaseBoundedBuffer<V> { private final V[] buf; private int tail; private int head

随机推荐