第十七讲 BlockingQueue
1 阻塞队列
队列:是一种数据结构,先进先出。排队。FIFO
对于普通的队列而言:如果队列空了,还要从队列中移除数据就会抛异常。
这种队列不适合用在高并发场景下。因为高并发场景下,
取数据是一条线程,写数据是另一条线程
取不到数据的时候,也就是队列空了
取线程应该阻塞,等待队列中有数据,一旦有数据,该线程被唤醒。
也就是说,get()不到的时候,应该wait()
一旦另外一条写线程put()成功,notify/notifyAll(),让阻塞的线程醒来去取数据
如果队列满了,写入队列的线程就应该阻塞wait(),等待读线程读取数据,读后通知写线程
进行put操作。
队列这玩意本来效率就差。但是它有序。我们看实现,如果用数组,查询效率高,增删效率差
如果是链表,查效率低,增删效率高。map无序。
2 普通队列线程不安全
public class QueueTest {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
list.push(i);
/*try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName() + " push" + i);
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " pop" + list.pop());
}
}, "B").start();
}
}
可能抛出一下异常:
Exception in thread "B" java.util.NoSuchElementException
这些线程不安全的队列,在高并发场景下,是不能使用的。
高并发场景下,如果要使用队列,就要使用线程安全的队列。
方式1:自己写线程安全的队列
方式2:使用API中线程安全的队列
API中线程安全的队列有哪些?
首先,我们要去java.util.concurrent下去看,因为这是java并发包
并发场景下用到的工具基本都在这里。
补充概念:
Queue:队列。先进先出。单向队列。吃了拉。
Stack:栈。先进后出。吃了吐。
Deque:队列。双向队列。上吐下泻。
Deque和Queue哪个功能更加强大?Deque。
如果你是SUN公司的java设计者,你会先设计Queue还是先设计Deque?
先设计强大的,因为复用,所以当要设计Queue的时候,我们可以直接用Deque
的方法进行适当的改造。(设计思想)
3 BlockingQueue
BlockingQueue:接口
1.它的实现类要掌握的主要有三个:
LinkedBlockingQueue : 链表的方式实现
ArrayBlockingQueue : 数组的方式实现
SynchronousQueue : 没有容量,不存储任何元素的阻塞队列。
2.他们实现线程安全的。线程安全涉及到什么?锁!也就是说这个类设计的时候已经考虑了
锁的问题。
3.剖析一下ArrayBlockingQueue
3.1 类结构:实现了BlockingQueue接口,继承AbstractQueue类
3.2 成员属性:
final Object[] items; 本质上是一个数组
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
transient Itrs itrs = null;
take()和put()是阻塞。
超时 poll()移除和offer()插入
public class BlockingQueueTest implements Serializable {
private static final long serialVersionUID = -6267964323673681096L;
public static void main(String[] args) {
BlockingQueue bq = new SynchronousQueue();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
bq.put(i);
System.out.println("put 成功" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"put").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
System.out.println(bq.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"take").start();
}
}