教会你实现Java的同步队列

在Java中,实现一个同步队列(BlockingQueue)可以帮助我们处理多线程情况下的任务传递。我们将分步骤来实现一个简单的同步队列。以下是实现的流程,如下表所示:

步骤 描述
1 创建队列的基本结构
2 实现入队功能
3 实现出队功能
4 添加线程同步
5 测试同步队列的功能

步骤详细说明

1. 创建队列的基本结构

首先,我们需要定义同步队列的基本结构,包括一个数组来存储队列元素以及必要的变量来跟踪队列的状态。

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

public class SyncQueue<T> {
    private Object[] items; // 存储队列元素的数组
    private int putIndex;   // 入队索引
    private int takeIndex;  // 出队索引
    private int count;      // 当前队列元素数量
    private final Lock lock; // 锁对象
    private final Condition notEmpty; // 出队条件
    private final Condition notFull;  // 入队条件

    public SyncQueue(int capacity) {
        this.items = new Object[capacity];  // 初始化队列数组
        this.lock = new ReentrantLock();     // 初始化锁
        this.notEmpty = lock.newCondition();  // 初始化出队条件
        this.notFull = lock.newCondition();   // 初始化入队条件
    }
}
2. 实现入队功能

接下来,我们实现一个入队方法,该方法将元素添加到队列中,并在队列满时等待。

public void put(T item) throws InterruptedException {
    lock.lock(); // 获取锁
    try {
        while (count == items.length) { // 判断队列是否满
            notFull.await(); // 如果满,则等待
        }
        items[putIndex] = item; // 将元素放入队列
        if (++putIndex == items.length) putIndex = 0; // 循环使用数组
        count++; // 元素数量增加
        notEmpty.signal(); // 唤醒出队线程
    } finally {
        lock.unlock(); // 释放锁
    }
}
3. 实现出队功能

然后,我们需要实现出队方法,从队列中提取元素,并在队列为空时等待。

public T take() throws InterruptedException {
    lock.lock(); // 获取锁
    try {
        while (count == 0) { // 判断队列是否为空
            notEmpty.await(); // 如果为空,则等待
        }
        @SuppressWarnings("unchecked")
        T item = (T) items[takeIndex]; // 从队列中取出元素
        items[takeIndex] = null; // 清空该位置
        if (++takeIndex == items.length) takeIndex = 0; // 循环使用数组
        count--; // 元素数量减少
        notFull.signal(); // 唤醒入队线程
        return item; // 返回取出的元素
    } finally {
        lock.unlock(); // 释放锁
    }
}
4. 添加线程同步

通过使用ReentrantLockCondition对象,我们确保了在多线程环境下的安全性,避免数据竞争。

5. 测试同步队列的功能

最后,我们可以编写一个简单的测试类,通过多个线程来测试我们的同步队列。

public class SyncQueueTest {
    public static void main(String[] args) {
        SyncQueue<Integer> queue = new SyncQueue<>(5);
        
        // 生产者线程
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    System.out.println("Producing: " + i);
                    queue.put(i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 消费者线程
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    int item = queue.take();
                    System.out.println("Consuming: " + item);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

结尾

现在,你应该已经掌握了如何在Java中实现一个简单的同步队列。通过上述步骤和代码示例,我们能够确保在多线程环境下安全地进行数据交换。在实际应用中,BlockingQueue可以帮助我们实现更复杂的生产者—消费者模型。希望这篇文章能帮助你更好地理解和实现同步队列!

类图

classDiagram
    class SyncQueue {
        +put(item)
        +take()
    }

旅行图

journey
    title SyncQueue执行过程
    section 生产者线程
      生产数据: 5: 成功
      入队: 5: 成功
      生产数据: 6: 成功
      入队: 6: 成功
    section 消费者线程
      出队: 5: 成功
      出队: 6: 成功