Java数据结构11-死磕Java队列-LinkedList

7 篇文章 0 订阅
5 篇文章 0 订阅

1. 背景

这里说的是 Java 的有界非阻塞队列,如果对有界非阻塞所具有的概念不太懂的话,可以参看 [[Java数据结构10-死磕Java队列-基础篇]] 。

本人对基本原理以及概念说的偏少,阐述的是应用过程,和一些使用上的注意事项。

2. LinkedList

List 接口与 Queue 同一级别,继承自 Collection 接口, LinkedList 实现了 Deque 接口。因为它的实现是双向链表,并没有初始化大小,更没有扩容的机制。

类继承图

2.1. 说明

通常适用与插入元素和删除元素,时间的复杂度 O(1) 。它性能高效的原理是通过链表实现,插入和删除只需要改变前后两个节点指针指向即可。

2.2. 构造函数

LinkedList 提供了两种构造函数,分别为无参、有参。有参的构造函数提供的参数为几个集合。


/**
    * Constructs an empty list.
    */
public LinkedList() {
}

/**
    * Constructs a list containing the elements of the specified
    * collection, in the order they are returned by the collection's
    * iterator.
    *
    * @param  c the collection whose elements are to be placed into this list
    * @throws NullPointerException if the specified collection is null
    */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

2.3. 常用方法

方法名描述
element检索但不删除此列表的头(第一个元素)
getFirst返回此列表中的第一个元素
getLast返回此列表中的最后一个元素
offer将指定的元素添加为此列表的尾部
poll检索并删除此列表的头(第一个元素)
pop从此列表表示的堆栈中弹出一个元素,换句话说,删除并返回此列表的第一个元素
peek检索但不删除此列表的头(第一个元素)

2.4. 样例

2.4.1. 单线程

此样例,我们创造一个队列,先查看它的头元素,然后再消费一条,再次查看它头元素的变化。


public static void main(String[] args) {
        Queue<String> queue = new LinkedList<String>();
        initQueue(queue);
        for(String q : queue){
            System.out.print(q+"\t");
        }
        System.out.print("\n");
        System.out.println(" 队列中头信息element: "+queue.element());
        System.out.println(" 获取信息poll: " + queue.poll());
        System.out.println(" 队列中头信息element: "+queue.element());
        System.out.println(" ============ ");
        System.out.println(" 队列中头信息peek: "+queue.peek());

        for(String q : queue){
            System.out.print(q+"\t");
        }
        System.out.print("\n");
    }

    /** 初始化队列中的数据
     * @Description
     * @param queue
     * @return void
     * @throws
     * @date 2020/8/5 15:12
     */
    public static void initQueue(Queue<String> queue){
        queue.offer("Q1");
        queue.offer("Q2");
        queue.offer("Q3");
        queue.offer("Q4");
        queue.offer("Q5");
    }
    

我们 poll 操作后,元素的数量发生了变化。这是我们队列的最最基础的使用形式,满足常用的顺序性消费场景,解决一般的线性问题。

20221001091911

2.4.2. 多线程

实际运用中,我们将采用一个线程模拟生产者不断对队列进行创造,同时给队列设定一个容量的上限,超过队列容量,将对生产者线程进行挂起;一个线程模拟消费者对队列进行消费,当队列中没有可供元素进行消费,需要对消费者线程进行挂起。

因为 LinkedList 的特性,当消费者没有获取到元素,此时消费者线程不会阻塞,而是直接返回,这就是非阻断,此处强调下,因为后续阻塞队列与它有明确的区别。

2.4.3. 队列操作

public class QueueUtil {

    LinkedList<String> queue;

    private final static int size = 15;

    QueueUtil(LinkedList<String> queue) {
        this.queue = queue;
    }

    public void add(String val) throws InterruptedException {
        synchronized (queue) {
            if (queue.size() == size) {
                System.out.println(Thread.currentThread().getName() + "队列已经满");
                queue.wait();
            } else {
                queue.offer(val);
                queue.notify();
            }
        }
    }

    public String romove() throws InterruptedException {
        synchronized (queue) {
            String val = null;
            if (queue.size() == 0) {
                System.out.println(Thread.currentThread().getName() + "队列内容不足");
                queue.wait();
            } else {
                val = queue.pop();
                System.out.println(Thread.currentThread().getName() + "队列内容弹出"+ val);
                queue.notify();
            }
            return val;
        }
    }

}

2.4.3.1. 生产者

class ProducerCase implements Runnable {
    private QueueUtil util;

    ProducerCase(QueueUtil util){
        this.util=util;
    }

    @SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            util.add("Qu_"+i);
            if(i%3==0){
                TimeUnit.SECONDS.sleep(2);
            }
        }
    }
}

2.4.3.2. 消费者

class ConsumberCase implements Runnable {

    private QueueUtil util;

    ConsumberCase(QueueUtil util){
        this.util=util;
    }

    @SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            String val = util.romove();
            System.out.println(Thread.currentThread().getName() + "[ConsumberCase ]" + (val==null?"=================":val));
        }
    }
}


2.4.3.3. Main方法

public class MulitQueue {

    public static void main(String[] args) {
        LinkedList<String> queue = new LinkedList<>();
        QueueUtil util = new QueueUtil(queue);
        ExecutorService exe= Executors.newFixedThreadPool(6);

        for (int i = 0; i < 2; i++) {
            exe.submit(new ProducerCase(util));
        }

        for (int i = 0; i < 2; i++) {
            exe.submit(new ConsumberCase(util));
        }

        exe.shutdown();
    }
}

2.4.3.4. 日志

> Task :MulitQueue.main()
pool-1-thread-3队列内容弹出Qu_0
pool-1-thread-4队列内容弹出Qu_0
pool-1-thread-4[ConsumberCase ]Qu_0
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]Qu_0
pool-1-thread-3队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-3队列内容弹出Qu_1
pool-1-thread-4[ConsumberCase ]=================
pool-1-thread-4队列内容弹出Qu_1
pool-1-thread-3[ConsumberCase ]Qu_1
pool-1-thread-4[ConsumberCase ]Qu_1
pool-1-thread-3队列内容弹出Qu_2
pool-1-thread-3[ConsumberCase ]Qu_2
pool-1-thread-4队列内容弹出Qu_3
pool-1-thread-4[ConsumberCase ]Qu_3
pool-1-thread-3队列内容弹出Qu_2
pool-1-thread-3[ConsumberCase ]Qu_2
pool-1-thread-4队列内容弹出Qu_3
pool-1-thread-4[ConsumberCase ]Qu_3
pool-1-thread-3队列内容不足
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-4[ConsumberCase ]=================
pool-1-thread-3队列内容弹出Qu_4
pool-1-thread-3[ConsumberCase ]Qu_4
pool-1-thread-4队列内容弹出Qu_4
pool-1-thread-4[ConsumberCase ]Qu_4
pool-1-thread-3队列内容弹出Qu_5
pool-1-thread-3[ConsumberCase ]Qu_5
pool-1-thread-4队列内容弹出Qu_6
pool-1-thread-4[ConsumberCase ]Qu_6
pool-1-thread-3队列内容弹出Qu_5
pool-1-thread-3[ConsumberCase ]Qu_5
pool-1-thread-4队列内容弹出Qu_6
pool-1-thread-4[ConsumberCase ]Qu_6
pool-1-thread-3队列内容不足
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-4[ConsumberCase ]=================

BUILD SUCCESSFUL in 9s
3 actionable tasks: 2 executed, 1 up-to-date
11:15:21: Task execution finished 'MulitQueue.main()'.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王老邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值