文章目录

  • 1. 接口Queue<E>
  • 1.1. 添加操作
  • 1.1.1. add
  • 1.1.2. offer
  • 1.2. 检索操作
  • 1.2.1. element
  • 1.2.2. peek
  • 1.3. 移除操作
  • 1.3.1. remove
  • 1.3.2. poll
  • 1.4. 总结
  • 2. 接口BlockingQueue<E>
  • 2.1. contains
  • 2.2. drainTo
  • 2.3. remainingCapacity 和 remove
  • 2.4. 等待操作
  • 2.4.1. 指定时间操作 offer 和 poll
  • 2.4.1.1. offer
  • 2.4.1.1. poll
  • 2.4.2. 不指定时间操作 put和 take
  • 3. 接口BlockingDeque<E>
  • 4. SynchronousQueue<E>
  • 5. AbstractQueue<E> 非阻塞队列
  • 6. 总结



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

1. 接口Queue<E>

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

Modifier and Type

Method and Description

boolean

add(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true在成功后返回 IllegalStateException如果当前没有可用空间,则抛出IllegalStateException。

E

element() 检索,但不删除,这个队列的头。

boolean

offer(E e) 如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。

E

peek() 检索但不删除此队列的头,如果此队列为空,则返回 null

E

poll() 检索并删除此队列的头,如果此队列为空,则返回 null

E

remove() 检索并删除此队列的头。

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

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. 总结

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

添加

检索

移除

抛出异常

add

element

remove

不抛出异常

offer

peek

poll

2. 接口BlockingQueue<E>

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

Modifier and Type

Method and Description

boolean

contains(Object o) 如果此队列包含指定的元素,则返回 true

int

drainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。

int

drainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。

boolean

offer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,等待指定的等待时间(如有必要)才能使空间变得可用。

E

poll(long timeout, TimeUnit unit) 检索并删除此队列的头,等待指定的等待时间(如有必要)使元素变为可用。

void

put(E e) 将指定的元素插入到此队列中,等待空间可用。

int

remainingCapacity() 返回该队列最好可以(在没有存储器或资源约束)接受而不会阻塞,或附加的元素的数量 Integer.MAX_VALUE如果没有固有的限制。

boolean

remove(Object o) 从该队列中删除指定元素的单个实例(如果存在)。

E

take() 检索并删除此队列的头,如有必要,等待元素可用。

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. 等待操作

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

添加

移除

指定时间

offer

poll

不指定时间

put

take

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 Type

Method and Description

void

addFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,抛出一个指定的元素 IllegalStateException如果当前没有空间可用。

void

addLast(E e) 在插入如果它是立即可行且不会违反容量限制,抛出此双端队列的末尾指定元素 IllegalStateException如果当前没有空间可用。

Iterator<E>

iterator() 以正确的顺序返回此deque中的元素的迭代器。

boolean

offerFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,返回指定的元素 true在成功和 false ,如果当前没有空间可用。

boolean

offerFirst(E e, long timeout, TimeUnit unit) 在此deque的前面插入指定的元素,等待指定的等待时间(如果需要空间可用)。

boolean

offerLast(E e) 插入此双端队列的末尾,如果它是立即可行且不会违反容量限制,返回指定的元素 true在成功和 false ,如果当前没有空间可用。

boolean

offerLast(E e, long timeout, TimeUnit unit) 在此deque的末尾插入指定的元素,如果需要空间可用,等待指定的等待时间。

E

pollFirst(long timeout, TimeUnit unit) 检索并删除此deque的第一个元素,等待指定的等待时间(如有必要),使元素变为可用。

E

pollLast(long timeout, TimeUnit unit) 检索并删除此deque的最后一个元素,等待到指定的等待时间,如果需要,元素可用。

void

putFirst(E e) 在此deque的前面插入指定的元素,如有必要,等待空格变为可用。

void

putLast(E e) 在此deque的末尾插入指定的元素,如有必要,等待空格变为可用。

boolean

removeFirstOccurrence(Object o) 从此deque中删除指定元素的第一个出现。

boolean

removeLastOccurrence(Object o) 从此deque中删除指定元素的最后一次出现。

int

size() 返回此deque中的元素数。

E

takeFirst() 检索并删除此deque的第一个元素,如有必要等待,直到元素可用。

E

takeLast() 检索并删除此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 Type

Method and Description

void

clear() 什么也没做。

boolean

contains(Object o) 始终返回 false

boolean

containsAll(Collection<?> c) 返回 false ,除非给定的集合为空。

int

drainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。

int

drainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。

boolean

isEmpty() 始终返回 true

Iterator<E>

iterator() 返回一个空的迭代器,其中 hasNext总是返回 false

boolean

offer(E e) 如果另一个线程正在等待接收,则将指定的元素插入到此队列中。

boolean

offer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,如果需要,等待另一个线程接收到的指定等待时间。

E

peek() 始终返回 null

E

poll() 如果另一个线程正在使一个元素可用,则检索并删除此队列的头。

E

poll(long timeout, TimeUnit unit) 检索并删除此队列的头,如果需要等待指定的等待时间,另一个线程插入它。

void

put(E e) 将指定的元素添加到此队列,等待另一个线程接收它。

int

remainingCapacity() 始终返回零。

boolean

remove(Object o) 总是返回 false

boolean

removeAll(Collection<?> c) 总是返回 false

boolean

retainAll(Collection<?> c) 总是返回 false

int

size() 始终返回零。

Spliterator<E>

spliterator() 返回一个空的拼写器,其中对 Spliterator.trySplit()的调用总是返回 null

E

take() 检索并删除此队列的头,等待另一个线程插入它。

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 Type

Method and Description

boolean

add(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作而不违反容量限制, 在成功后返回 true,如果当前没有可用空间,则抛出IllegalStateException。

boolean

addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此队列中。

void

clear() 从此队列中删除所有元素。

E

element() 检索,但不删除,这个队列的头。

E

remove() 检索并删除此队列的头。

测试案例

@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. 总结

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

java如何使用队列处理请求 java怎么使用队列_抛出异常

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