并发队列Queue:阻塞队列
队列:底层由链表实现;
BlockingQueue:阻塞队列,当要取一个数据时,如果队列中没有,就会一直等待。
当要存一个数据时,如果数据满了,也会一直等待,直到队列有空出来为止。
方法介绍:put,往队列中添加一个数,如果队列已经满了,就堵塞住;
take 在队列中拿出一个数,如果队列是空的,就堵塞住;
add 往队列中添加一个数,如果队列已经满了,会抛出异常
remove 往队列中移除一个数,如果队列空了,会抛出异常
element 查看队列中第一个数,不移除,如果是空的,抛出异常
offer 往队列中添加一个数,如果队列是满的,返回false
poll 在队列中拿出一个数,并进行移除,如果队列是空的,返回null
peek 查看队列中一个数,如果空,返回为null
下面是BlockingQueue的子类
代码分析:
package collections.queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ArrayBlockingQueueDemo {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
Interviewer r1 = new Interviewer(queue);
Consumer r2 = new Consumer(queue);
new Thread(r1).start();
new Thread(r2).start();
}
}
// 充当生产者
class Interviewer implements Runnable {
BlockingQueue queue;
public Interviewer(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
System.out.println("十个候选人都来了");
for (int i = 0; i < 10; i++) {
String candidate = "Candidate" + i;
try {
queue.put(candidate);
System.out.println("安排好了" + candidate);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
queue.put("stop");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 充当消费者
class Consumer implements Runnable {
BlockingQueue<String> queue;
public Consumer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
String msg;
while (!(msg = queue.take()).equals("stop")) {
System.out.println(msg + "到了");
Thread.sleep(1000);
}
System.out.println("所有候选人都结束了");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
}
}
结论如下:
put方法的源码分析:
这是BlockingQueue里的put实现方式,但是,我们想看的是ArrayBlockingQueue中的put方法。对此,我们可以使用ctrl + Alt + 鼠标左键 来进行查看;
checkNotNull字面理解就是对e的非空检查,如果是空的话,就会报错;
下面加锁,方法是lockInterruptible();说明是可以暂停的
如果队列中已经满了,就等待;
如果没有满,就入队;
通过查询源码
ArrayBlockingQueue队列底层是Object[] ;
LinkedBlockingQueue
点击ctrl + shift + N
首先看到的Node代表的是它的节点,所以,LinkedBlockingQueue底层是由链表实现的。
然后,我们会发现它有两把锁
先获取put锁,如果队列中元素已经满了,就阻塞,如果队列中没满,就去加入,然后进行判断,如果还有空的,就再唤醒一个线程。
它是无界的,所以,put的时候不会塞满。自己思考:上面的LinkedBlockQueue 会堵塞,因为代码中有体现。而这里的PriorityQueue是不会堵塞的。
它是直接进行传递的。
其add的代码如下:如果p.casNext是成功的,那么就进行增加。如果失败了,跳回for语句进行二次加入。
第一:根据需求,有边界的和无边界的,就像LinkedBlockingQueue,和priorityBlockingQueue是无边界的。像ArrayBlockingQueue,SynchronousQueue是有边界的。
第二:空间,像synchronousQueue,都不占空间,ArrayBlockingQueue其内部是Object[]从内存上将,它比较整齐,而LinkedBlockingQueue其底层是由链表实现的,所以,相对来说就会乱一些。
第三:吞吐量,从性能角度来讲,LinkedBlockingQueue的吞吐量一般优于ArrayBlockingQueue,因为它有两把锁。它锁的粒度更加的细致,他有takelock,和putlock两把锁。而ArrayBlockingQueue是一种基础的能适应大多数情况的,稳定的选择。
SynchronousQueue,如果直接交换,而不需要存储,就可以选择这个。
Concurrent的特点是大部分通过CAS实现,比如说,ConcurrentHashMap,就是通过比较交换的CAS实现的。
CopyOnWrite 的特点是大部分通过赋值一份实现的,
Blocking*的特点所示通过AQS实现的,等会会进行说明