Java 队列使用


在java的数据结构中有一个数据结构就是队列,在这个特性里,Java提供了一些不同特点的队列面对不同的场景使用,这里做一个简单的总结。

1. 接口Queue<E>

这个是所有队列的父级接口,他提供了最基本的六个操作接口,这六个接口如下

Modifier and TypeMethod and Description
booleanadd(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true在成功后返回 IllegalStateException如果当前没有可用空间,则抛出IllegalStateException。
Eelement() 检索,但不删除,这个队列的头。
booleanoffer(E e) 如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。
Epeek() 检索但不删除此队列的头,如果此队列为空,则返回 null
Epoll() 检索并删除此队列的头,如果此队列为空,则返回 null
Eremove() 检索并删除此队列的头。

下面通过一个阻塞队列测试上面的六种方法

1.1. 添加操作

在添加操作中提供了两个方法,一个add方法和一个offer方法。

1.1.1. add

这个方法在超出容量的时候或者说添加失败的时候会抛出异常;

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));
        log.info(String.valueOf(blockingQueue.add("4")));
    }
}

打印结果

Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
	at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
	at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:21)
14:41:22.379 [main] INFO juc.queue.TestBlockingQueue - true
14:41:22.381 [main] INFO juc.queue.TestBlockingQueue - true
14:41:22.381 [main] INFO juc.queue.TestBlockingQueue - true

这里提示队列满了,添加失败

1.1.2. offer

在添加失败后返回false,不会抛出异常

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.offer("1")));
        log.info(String.valueOf(blockingQueue.offer("2")));
        log.info(String.valueOf(blockingQueue.offer("3")));
        log.info(String.valueOf(blockingQueue.offer("4")));
    }
}

打印结果:

14:45:34.479 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - false

和上面一样的操作,但是当队列满了后,添加失败,但是没有抛出异常,而是返回false

1.2. 检索操作

在队列中提供了两个用于检索头部元素的方法,检索方法在查询的时候是不会移除元素的,element和peek方法;

案例如下:

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));

        log.info(String.valueOf(blockingQueue.element()));
        log.info(String.valueOf(blockingQueue.peek()));
    }
}

测试结果:

14:51:12.139 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - 1
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - 1

这两个方法只是访问了头部元素,但是没有移除元素;

这两个不同的地方是检索不成功的响应

1.2.1. element

这个方法在访问空的头部的时候会抛出异常:

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.element()));
    }
}

测试结果:

Exception in thread "main" java.util.NoSuchElementException
	at java.util.AbstractQueue.element(AbstractQueue.java:136)
	at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:18)

这里抛出了NoSuchElementException异常

1.2.2. peek
@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.peek()));
    }
}

测试结果:

[main] INFO juc.queue.TestBlockingQueue - null

这里返回的是一个null,没有抛出异常

1.3. 移除操作

队列也提供了俩个移除操作,分别的remove和poll方法,这两个方法在检索的同时也会移除检索的数据,下面案例:

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));

        log.info(String.valueOf(blockingQueue.remove()));
        log.info(String.valueOf(blockingQueue.poll()));

        // 查看此时的头部元素
        log.info(String.valueOf(blockingQueue.element()));
    }
}

测试结果:

15:01:28.546 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 1
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 2
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 3

从这里可以看出来返回了检索的结果,同时也移除了这个值。

但是这个两个不同在于面对移除对象不存在时的反应

1.3.1. remove

这个方法在访问null的时候会抛出异常

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.remove()));
    }
}

测试结果:

Exception in thread "main" java.util.NoSuchElementException
	at java.util.AbstractQueue.remove(AbstractQueue.java:117)
	at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:18)

这里返回了和检索的时候element方法一样的异常

1.3.2. poll

这个方法面对null的时候不会抛出异常,只会返回null

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        log.info(String.valueOf(blockingQueue.poll()));
    }
}

测试结果:

[main] INFO juc.queue.TestBlockingQueue - null

这里返回的是null,没有抛出异常

1.4. 总结

这里通过一个表格来说明这个问题:

添加检索移除
抛出异常addelementremove
不抛出异常offerpeekpoll

2. 接口BlockingQueue<E>

在聊到队列的时候一定离不开这个阻塞队列接口,这个接口在继承了Queue接口后提供了这几个方法,下面说一下

Modifier and TypeMethod and Description
booleancontains(Object o) 如果此队列包含指定的元素,则返回 true
intdrainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。
intdrainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。
booleanoffer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,等待指定的等待时间(如有必要)才能使空间变得可用。
Epoll(long timeout, TimeUnit unit) 检索并删除此队列的头,等待指定的等待时间(如有必要)使元素变为可用。
voidput(E e) 将指定的元素插入到此队列中,等待空间可用。
intremainingCapacity() 返回该队列最好可以(在没有存储器或资源约束)接受而不会阻塞,或附加的元素的数量 Integer.MAX_VALUE如果没有固有的限制。
booleanremove(Object o) 从该队列中删除指定元素的单个实例(如果存在)。
Etake() 检索并删除此队列的头,如有必要,等待元素可用。

2.1. contains

这个方法非常好理解,就是检查十分包含指定元素

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));

        log.info("===========================================");

        log.info(String.valueOf(blockingQueue.contains("2")));
        log.info(String.valueOf(blockingQueue.contains("4")));
    }
}

测试结果:

15:40:31.832 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - ===========================================
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - false

2.2. drainTo

这个方法有一个两个重载方法,作用是取队列里的元素到给定的容器中。

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue1 = new ArrayBlockingQueue(3);
        BlockingQueue<String> blockingQueue2 = new ArrayBlockingQueue(3);
        log.info(String.valueOf(blockingQueue1.add("1")));
        log.info(String.valueOf(blockingQueue1.add("2")));
        log.info(String.valueOf(blockingQueue1.add("3")));

        log.info(String.valueOf(blockingQueue2.add("1")));
        log.info(String.valueOf(blockingQueue2.add("2")));
        log.info(String.valueOf(blockingQueue2.add("3")));

        log.info("===========================================");
        // 取出队列中所有的元素,返回取出元素的个数
        List<String> list1 = new ArrayList<>();
        log.info(String.valueOf(blockingQueue1.drainTo(list1)));

        // 取出队列中指定个数的元素,返回取出元素的个数
        List<String> list2 = new ArrayList<>();
        List<String> list3 = new ArrayList<>();
        log.info(String.valueOf(blockingQueue2.drainTo(list2,2)));
        log.info(String.valueOf(blockingQueue2.drainTo(list3,2)));

        log.info(JSON.toJSONString(list1));
        log.info(JSON.toJSONString(list2));
        log.info(JSON.toJSONString(list3));
    }
}

测试结果

15:48:28.827 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - ===========================================
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 3
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 2
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 1
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["1","2","3"]
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["1","2"]
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["3"]

从这个结果可以看出区别。

2.3. remainingCapacity 和 remove

这两个方法remainingCapacity 是计算剩余空间的,remove是原来方法的重载,用于指定删除元素

下面一个例子说明

@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));

        log.info("===========================================");
        // 获取剩余空间
        log.info(String.valueOf(blockingQueue.remainingCapacity()));
        // 删除指定元素
        log.info(String.valueOf(blockingQueue.remove("2")));
        // 判断指定元素是否删除成功
        log.info(String.valueOf(blockingQueue.contains("2")));
        // 获取删除后队列的剩余空间
        log.info(String.valueOf(blockingQueue.remainingCapacity()));
    }
}

测试结果:

[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - ===========================================
[main] INFO juc.queue.TestBlockingQueue - 0
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - false
[main] INFO juc.queue.TestBlockingQueue - 1

2.4. 等待操作

在阻塞队列中提供了两组添加和移除的阻塞等待操作,一组可以指定延时时间,一组不能指定时间,会一直阻塞下去。

添加移除
指定时间offerpoll
不指定时间puttake
2.4.1. 指定时间操作 offer 和 poll

这两个方法是Queue接口的方法重载,可以设置最大阻塞时间

2.4.1.1. offer
@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
        log.info(String.valueOf(blockingQueue.add("1")));
        log.info(String.valueOf(blockingQueue.add("2")));
        log.info(String.valueOf(blockingQueue.add("3")));

        new Thread(() -> {
            try {
                log.info(String.valueOf(blockingQueue.offer("4",1, TimeUnit.SECONDS)));
                TimeUnit.SECONDS.sleep(2);
                log.info(String.valueOf(blockingQueue.offer("4",2, TimeUnit.SECONDS)));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ).start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                log.info(String.valueOf(blockingQueue.remove()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ).start();
    }
}

测试结果:

[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[Thread-0] INFO juc.queue.TestBlockingQueue - false
[Thread-1] INFO juc.queue.TestBlockingQueue - 1
[Thread-0] INFO juc.queue.TestBlockingQueue - true

从上面可以看出,开始执行offer时阻塞1秒,发现无法添加后睡两秒,线程2在睡了4秒后移除一个元素,此时,线程1的offfer处于阻塞状态,发现移除一个元素了,有空间插入了,于是立即插入。第二次offer设置等待时间是2秒,实际等待了1秒,所有在最大允许的时间内,于是插入成功了,第一个则是等待1一秒后也没有空间插入,超时了,所以失败了。

2.4.1.1. poll
@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                log.info(String.valueOf(blockingQueue.add("1")));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ).start();

        new Thread(() -> {
            try {
                log.info(String.valueOf(blockingQueue.poll(1,TimeUnit.SECONDS)));
                log.info(String.valueOf(blockingQueue.poll(2,TimeUnit.SECONDS)));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ).start();
    }
}

测试结果:

16:29:47.745 [Thread-1] INFO juc.queue.TestBlockingQueue - null
16:29:48.741 [Thread-1] INFO juc.queue.TestBlockingQueue - 1
16:29:48.741 [Thread-0] INFO juc.queue.TestBlockingQueue - true

从上面可以看出来,线程1取数据的时候等待一秒后发现没有值,所以取出了null,然后又设置了一个等待两秒的移除操作,线程0在睡两秒后添加了元素,此时等待的移除操作,立即执行了取,于是取出来元素。

2.4.2. 不指定时间操作 put和 take
@Slf4j
public class TestBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(2);

        new Thread(() -> {
            try {
                log.info("阻塞获取元素 {}", blockingQueue.take());
                TimeUnit.SECONDS.sleep(5);
                log.info("等待5秒后移除一个元素 {}", blockingQueue.remove());
                TimeUnit.SECONDS.sleep(1);
                log.info("查看阻塞添加的元素 {}", blockingQueue.contains("4"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
                , "A").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                log.info("等待1秒后添加一个元素 {}", blockingQueue.add("1"));
                log.info("等待1秒后添加一个元素 {}", blockingQueue.add("2"));
                TimeUnit.SECONDS.sleep(1);
                log.info("再等待1秒后添加一个元素 {}", blockingQueue.add("3"));
                log.info("阻塞添加一个元素");
                blockingQueue.put("4");
                log.info("阻塞添加一个元素成功");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
                , "B").start();
    }
}

测试结果:

[A] INFO juc.queue.TestBlockingQueue - 阻塞获取元素 1
[B] INFO juc.queue.TestBlockingQueue - 等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 再等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 阻塞添加一个元素
[A] INFO juc.queue.TestBlockingQueue - 等待5秒后移除一个元素 2
[A] INFO juc.queue.TestBlockingQueue - 查看阻塞添加的元素 true
[B] INFO juc.queue.TestBlockingQueue - 阻塞添加一个元素成功

查看结果会发现和现实不同,原因是日志输出在多线程下顺序有些颠倒,但是逻辑是对的,首先阻塞等待获取元素,1秒后添加元素,等待获取的操作会立即获取添加的元素,然后把队列填充满,再阻塞等待添加,等待5秒后移除一个元素,有空间了此时等待添加的操作会立即添加元素到队列中,然后检查这个元素,会发现添加成功。

3. 接口BlockingDeque<E>

这个是一个双端队列,也就是说可以在头尾添加或移除操作,因为它继承了阻塞队列接口,所以她有BlockingQueue的方法,这里就不再做介绍了,这里介绍它的其他方法

Modifier and TypeMethod and Description
voidaddFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,抛出一个指定的元素 IllegalStateException如果当前没有空间可用。
voidaddLast(E e) 在插入如果它是立即可行且不会违反容量限制,抛出此双端队列的末尾指定元素 IllegalStateException如果当前没有空间可用。
Iterator<E>iterator() 以正确的顺序返回此deque中的元素的迭代器。
booleanofferFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,返回指定的元素 true在成功和 false ,如果当前没有空间可用。
booleanofferFirst(E e, long timeout, TimeUnit unit) 在此deque的前面插入指定的元素,等待指定的等待时间(如果需要空间可用)。
booleanofferLast(E e) 插入此双端队列的末尾,如果它是立即可行且不会违反容量限制,返回指定的元素 true在成功和 false ,如果当前没有空间可用。
booleanofferLast(E e, long timeout, TimeUnit unit) 在此deque的末尾插入指定的元素,如果需要空间可用,等待指定的等待时间。
EpollFirst(long timeout, TimeUnit unit) 检索并删除此deque的第一个元素,等待指定的等待时间(如有必要),使元素变为可用。
EpollLast(long timeout, TimeUnit unit) 检索并删除此deque的最后一个元素,等待到指定的等待时间,如果需要,元素可用。
voidputFirst(E e) 在此deque的前面插入指定的元素,如有必要,等待空格变为可用。
voidputLast(E e) 在此deque的末尾插入指定的元素,如有必要,等待空格变为可用。
booleanremoveFirstOccurrence(Object o) 从此deque中删除指定元素的第一个出现。
booleanremoveLastOccurrence(Object o) 从此deque中删除指定元素的最后一次出现。
intsize() 返回此deque中的元素数。
EtakeFirst() 检索并删除此deque的第一个元素,如有必要等待,直到元素可用。
EtakeLast() 检索并删除此deque的最后一个元素,如有必要等待,直到元素可用。

这些方法就不一一演示了,这些基本都是阻塞队列方法的变种,实现头尾添加和移除操作,下面举一个例子说吗一些上面的部分方法。

@Slf4j
public class TestBlockingDeque {
    public static void main(String[] args) {
        BlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(4);
        log.info("添加 {}", blockingDeque.offerFirst("1"));
        log.info("添加 {}", blockingDeque.offerLast("2"));
        log.info("添加 {}", blockingDeque.offerFirst("3"));

        log.info("元素数量{}", blockingDeque.size());

        Iterator<String> iterator = blockingDeque.iterator();
        while (iterator.hasNext()){
            log.info("iterator {}", iterator.next());
        }

        log.info("移除 {}", blockingDeque.pollLast());
        log.info("移除 {}", blockingDeque.pollFirst());
        log.info("移除 {}", blockingDeque.pollLast());
    }
}

测试结果:

[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 元素数量3
[main] INFO juc.queue.TestBlockingDeque - iterator 3
[main] INFO juc.queue.TestBlockingDeque - iterator 1
[main] INFO juc.queue.TestBlockingDeque - iterator 2
[main] INFO juc.queue.TestBlockingDeque - 移除 2
[main] INFO juc.queue.TestBlockingDeque - 移除 3
[main] INFO juc.queue.TestBlockingDeque - 移除 1

从这个执行结果就可以看出这些方法是使用了,这里就不多说了。

4. SynchronousQueue<E>

其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量。 你不能peek在同步队列,因为一个元素,当您尝试删除它才存在; 您无法插入元素(使用任何方法),除非另有线程正在尝试删除它; 你不能迭代,因为没有什么可以迭代。 队列的头部是第一个排队的插入线程尝试添加到队列中的元素; 如果没有这样排队的线程,那么没有元素可用于删除,并且poll()将返回null 。 为了其他Collection方法(例如contains )的目的, SynchronousQueue充当空集合。 此队列不允许null元素。

使用这个的时候是没有设置容量的,连续添加需要有其他线程取完后才可以继续添加新元素;

Modifier and TypeMethod and Description
voidclear() 什么也没做。
booleancontains(Object o) 始终返回 false
booleancontainsAll(Collection<?> c) 返回 false ,除非给定的集合为空。
intdrainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。
intdrainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。
booleanisEmpty() 始终返回 true
Iterator<E>iterator() 返回一个空的迭代器,其中 hasNext总是返回 false
booleanoffer(E e) 如果另一个线程正在等待接收,则将指定的元素插入到此队列中。
booleanoffer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,如果需要,等待另一个线程接收到的指定等待时间。
Epeek() 始终返回 null
Epoll() 如果另一个线程正在使一个元素可用,则检索并删除此队列的头。
Epoll(long timeout, TimeUnit unit) 检索并删除此队列的头,如果需要等待指定的等待时间,另一个线程插入它。
voidput(E e) 将指定的元素添加到此队列,等待另一个线程接收它。
intremainingCapacity() 始终返回零。
booleanremove(Object o) 总是返回 false
booleanremoveAll(Collection<?> c) 总是返回 false
booleanretainAll(Collection<?> c) 总是返回 false
intsize() 始终返回零。
Spliterator<E>spliterator() 返回一个空的拼写器,其中对 Spliterator.trySplit()的调用总是返回 null
Etake() 检索并删除此队列的头,等待另一个线程插入它。
Object[]toArray() 返回零长度数组。
<T> T[]toArray(T[] a) 将指定数组的零元素设置为 null (如果数组的长度不为零)并返回。

从这里可以看到,许多方法都被禁止使用,返回的值是固定的,这里给一个使用案例,一个线程添加一个线程获取

@Slf4j
public class TestSynchronousQueue {
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                synchronousQueue.put("1");
                log.info("添加元素 1");
                log.info("检索当前元素 {}",synchronousQueue.peek());
                synchronousQueue.put("2");
                log.info("添加元素 2");
                log.info("检索当前元素 {}",synchronousQueue.peek());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "A").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                log.info("移除元素 {}", synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                log.info("移除元素 {}", synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "B").start();
    }
}

返回结果:

[A] INFO juc.queue.TestSynchronousQueue - 添加元素 1
[A] INFO juc.queue.TestSynchronousQueue - 检索当前元素 null
[B] INFO juc.queue.TestSynchronousQueue - 移除元素 1
[A] INFO juc.queue.TestSynchronousQueue - 添加元素 2
[B] INFO juc.queue.TestSynchronousQueue - 移除元素 2
[A] INFO juc.queue.TestSynchronousQueue - 检索当前元素 null

从这些结果可以看出,peek方法被禁止了,返回总是空,源码:

public E peek() {  return null;  }

返回的结果可以看出,当一个线程取出当前的数据,才能添加,这个队列只能存放一个元素。

5. AbstractQueue<E> 非阻塞队列

这个队列是不能使用阻塞方法的,它没有想阻塞队列一样的阻塞操作。它的操作都是实时的

Modifier and TypeMethod and Description
booleanadd(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作而不违反容量限制, 在成功后返回 true,如果当前没有可用空间,则抛出IllegalStateException。
booleanaddAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此队列中。
voidclear() 从此队列中删除所有元素。
Eelement() 检索,但不删除,这个队列的头。
Eremove() 检索并删除此队列的头。

测试案例

@Slf4j
public class TestAbstractQueue {
    public static void main(String[] args) {
        AbstractQueue<String>  abstractQueue = new ArrayBlockingQueue<>(4);
        log.info("{}", abstractQueue.add("1"));
        List<String> list = Arrays.asList(new String[]{"2","3","4"});
        log.info("{}", abstractQueue.addAll(list));
        log.info("{}", abstractQueue.element());
        log.info("{}", abstractQueue.remove());
        log.info("{}", abstractQueue.peek());
        abstractQueue.clear();
        log.info("{}", abstractQueue.peek());
    }
}

测试结果:

[main] INFO juc.queue.TestAbstractQueue - true
[main] INFO juc.queue.TestAbstractQueue - true
[main] INFO juc.queue.TestAbstractQueue - 1
[main] INFO juc.queue.TestAbstractQueue - 1
[main] INFO juc.queue.TestAbstractQueue - 2
[main] INFO juc.queue.TestAbstractQueue - null

这个案例说明了上面方法的使用,为什么这里要介绍这个队列,查看源码会发现阻塞队列是基于这个抽象类实现的,这个抽象类实现了add等抽象方法,告诉我们这个add方法是基于这个实现类的offer实现的。

6. 总结

这里介绍了一些主要的队列接口方法,通过他们也派生了许多其他的队列方法,这里就不过多的说明
在这里插入图片描述

从这幅图可以看出上面所说内容的关系了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值