Java自定义一个有序队列

1、简介与说明

这里主要是利用数组,实现一个线程安全的有界队列,不支持扩容。

  1. 数据结构:这里使用的数组
  2. 算法:FIFO,遵循队列先进先出原则
  3. 线程安全:利用synchronized关键字加锁以及线程之间通讯确保队列安全

2、具体代码实现

1、有界队列

/**
 * 定义一个线程安全有界消息队列
 * 1、数据结构:数组
 * 2、具体算法:FIFO,先进先出
 */
public class BlockContainer<T> {

    private Object[] array;

    private int size;

    /**
     * 提供带参构造
     *
     * @param cap
     */
    public BlockContainer(int cap) {
        array = new Object[cap];
    }

    /**
     * 提供默认构造,默认大小16
     */
    public BlockContainer() {
        this(16);
    }

    /**
     * 生产者线程通过put方法向队列存放消息
     * 数据永远放在size位置
     * 实力方法内部的this永远指向调用此方法的当前对象
     * 静态方法中没有this,this只能应用在实例方法,构造方法,实例代码块中
     */
    public synchronized void put(T t) {
        //1、判断队列大小是不是满了,满了的话就不接收消息了
        while (size == array.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、如果队列没有满,放入数据
        array[size] = t;
        //3、有效元素个数加一
        size++;
        //4、通知消费者取数据
        this.notifyAll();
    }

    /**
     * 消费者通过此方法取数据
     * 永远取下标为0的位置的数据
     *
     * @return
     */
    public synchronized T take() {
        //1、判断当前容器是否为空
        while (size == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2、取数据
        Object obj = array[0];
        //3、移动元素
        System.arraycopy(
                array, //原数组
                1,//从哪个位置开始拷贝
                array,//放到哪个数组
                0,//从哪个位置开始放
                size - 1);//拷贝几个
        //4、队列有效个数减一
        size--;
        //5、将size位置的值置为null
        array[size] = null;
        //6、唤醒所有线程取数据
        this.notifyAll();
        return (T) obj;
    }

    @Override
    public String toString() {
        return "BlockContainer{" +
                "array=" + Arrays.toString(array) +
                ", size=" + size +
                '}';
    }
}

2、测试

/**
 * 测试多线程情况下的队列
 */
public class Test02 {

    static class Producer extends Thread {
        private BlockContainer<Object> blockContainer;

        public Producer(BlockContainer<Object> blockContainer) {
            this.blockContainer = blockContainer;
        }

        @Override
        public void run() {
            int i = 1;
            while (true) {
                blockContainer.put(i);
                System.out.println("往队列中放入消息:[" + i + "]");
                i++;
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Consumer extends Thread {
        private BlockContainer<Object> blockContainer;

        public Consumer(BlockContainer<Object> blockContainer) {
            this.blockContainer = blockContainer;
        }

        @Override
        public void run() {
            while (true) {
                Object result = blockContainer.take();
                System.out.println("往队列中取出消息:[" + result + "]");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        BlockContainer<Object> c = new BlockContainer<>();
        Producer producer = new Producer(c);
        Consumer consumer = new Consumer(c);
        producer.start();
        consumer.start();
    }
}

最后,有好的想法和意见欢迎大家一起交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Java的ThreadPoolExecutor类来实现分组线程池,并保证分组内的任务有序执行。 首先,我们需要定义一个线程池组,用于将线程池中的线程分组。可以使用Map来实现,其中key为组名,value为对应的线程池。例如: ``` Map<String, ThreadPoolExecutor> threadPoolGroups = new HashMap<>(); ``` 同时,我们需要为每个线程池组创建一个BlockingQueue作为任务队列,用于存储待执行的任务,同时使用一个Map来记录每个组对应的任务队列。例如: ``` Map<String, BlockingQueue<Runnable>> taskQueues = new HashMap<>(); ``` 接下来,我们需要定义一个方法来创建线程池,并将其添加到对应的线程池组中。这个方法可以接收两个参数:组名和线程池配置参数。例如: ``` public void createThreadPool(String groupName, ThreadPoolExecutorConfig config) { BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(); ThreadPoolExecutor threadPool = new ThreadPoolExecutor(config.getCorePoolSize(), config.getMaxPoolSize(), config.getKeepAliveTime(), config.getTimeUnit(), taskQueue); threadPoolGroups.put(groupName, threadPool); taskQueues.put(groupName, taskQueue); } ``` 其中,ThreadPoolExecutorConfig是一个自定义的配置类,用于存储线程池的配置参数。 最后,我们可以定义一个方法来获取对应组名的任务队列,并将任务提交到这个任务队列中。例如: ``` public void submitTask(String groupName, Runnable task) { BlockingQueue<Runnable> taskQueue = taskQueues.get(groupName); if (taskQueue != null) { try { taskQueue.put(task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } ``` 接下来,我们需要定义一个方法来启动线程池,从任务队列中取出并执行任务。这个方法可以通过一个无限循环来实现,不断从任务队列中取出任务,并交给对应的线程池执行。例如: ``` public void start() { for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolGroups.entrySet()) { final String groupName = entry.getKey(); final ThreadPoolExecutor threadPool = entry.getValue(); final BlockingQueue<Runnable> taskQueue = taskQueues.get(groupName); new Thread(() -> { while (true) { try { Runnable task = taskQueue.take(); threadPool.execute(task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }).start(); } } ``` 这样,我们就可以根据需要将任务提交到不同的线程池组中,并保证同一组内的任务有序执行。注意,这里我们需要调用start()方法来启动线程池,否则任务将无法得到执行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值