- BlockingQueue阻塞队列
- 和LinkedBlockingQueue区别)">ConcurrentLinkedQueue(和LinkedBlockingQueue区别)
- Queue中提供的一些多线程比较友好的接口(ConcurrentQueue的常用接口)
- offer—->相当于List的add,可以用offer的**返回值来判断是不是添加成功**==
- poll—->取数据
- peek—->拿数据
- 以上全是线程安全的操作
- 具体区别
- add加不进去了,会抛异常;而offer不管怎样都会给你一个返回值(true、false);现在都用offer
- peek去取不会remove掉
- poll去取并且remove掉
- 这几个操作在BlockingQueue中都是线程安全的操作(add是不是???)
BlockingQueue阻塞队列
- 提供了一系列的方法,可以实现线程自动阻塞
- BlockingQueue在Queue的基础上又添加了两个方法put、take;这两个方法真正实现了阻塞
- put表示一定要往里面装,如果满了,这个线程会阻塞住
- take表示往外取,如果空了的话,这个线程会阻塞住
- 即实现了生产者、消费者里面的那个队列容器,
- BlockingQueue天生就是对**多线程友好**的生产者、消费者模型,天生实现了生产者、消费者这个模型
- 阻塞的原理:和之前写的小程序没有太大的区别
- LockSupport的park方法—->是阻塞的概念,不是锁的概念—->进入一个wait状态,**不是锁**,只是阻塞当前线程
- wait、notify和await、signal
- ReentrantLock
- ……
- BlockingQueue提供了很多在多线程情况下友好的api
- put和take实现了阻塞,可以自然而然地实现任务队列,生产者消费者模型—->多线程中最重要的模型
- 5中的模型本质上是MQ(Message Queue)的基础,MQ本质上就是一个大型的生产者消费者模型(必考、必问)
LinkedBlockingQueue
- ❌链表实现的无界队列
- ❌可以向里面一直装,直到内存溢出,没有长度限制
- LinkedBlockingQueue不是无界队列,他的最大值是Integer的最大值,说无界队列不精确
- LinkedBlockingQueue不能一直往里面扔直到内存崩了为止,超过最大值也就满了
ArrayBlockingQueue
- 有界的BlockingQueue
- 可以指定初始容器的容量大小
- 一旦满了之后程序就会阻塞住,阻塞在put处,等待消费者消费
- 在满了的情况下用add会报异常
- 不管满不满offer都会及时返回,成功返回true,失败返回false;用返回时来判断有没有加成功
- offer在加入的时候还可以带个时间,可以尝试一下往里面加1s;1s后加不进去就返回false,如果能加入就等1s然后返回;阻塞1s后加
DelayQueue(实现时间上的排序)
SynchronousQueue(用在线程之间传递任务上会很方便)
- 专门用在两线程之间传递任务
- 可以用在给线程传递任务 ```java package com.mashibing.juc.c_025;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue;
public class T08_SynchronusQueue { //容量为0
public static void main(String[] args) throws InterruptedException {
BlockingQueue
new Thread(()->{
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strs.put("aaa"); //阻塞等待消费者消费
//strs.put("bbb");
//strs.add("aaa");
System.out.println(strs.size());
}
}
<a name="Mllak"></a>
## TransferQueue
> 1. 是前面几种Queue的组合,但是又有些区别
> 1. 可以给线程传递任务,但不止能传递一个任务(SynchronousQueue只能传递一个任务)
> 1. TransferQueue中是一个链表,可以传好多个
> 1. transfer方法装完,就阻塞在那里,直到有人将其取走,他才会继续干下面的事情(装完取走付钱,必须付钱之后才能取走),transfer不满也会等着,来了就等着
> 1. 底层有一个比较好玩的机制:消费者没有东西消费的时候放空蓝子???等着生产者往里面装
> 1. SynchronousQueue中没有空篮子,因为他是线程之间手递手传递
> 1. TransferQueue是多人对多人的手递手传递
```java
package com.mashibing.juc.c_025;
import java.util.concurrent.LinkedTransferQueue;
public class T09_TransferQueue {
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<String> strs = new LinkedTransferQueue<>();
// 必须先有消费者去获取,否则直接上生产者的话会造成阻塞在那里,不能往下执行
new Thread(() -> {
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strs.transfer("aaa");
//strs.put("aaa");
/*new Thread(() -> {
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();*/
}
}
ConcurrentLinkedQueue(和LinkedBlockingQueue区别)
- Concurrent与Blocking一般不连用
- 并发就是并发,直接就可以了
- 阻塞就是阻塞,不是并发
- 可以采用这种简单的不加锁的方式,效率会高上很多
/**
* 有N张火车票,每张票都有一个编号
* 同时有10个窗口对外售票
* 请写一个模拟程序
*
* 分析下面的程序可能会产生哪些问题?
* 重复销售?超量销售?
*
* 使用Vector或者Collections.synchronizedXXX
* 分析一下,这样能解决问题吗?
*
* 就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
* 就像这个程序,判断size和进行remove必须是一整个的原子操作
*
* 使用ConcurrentQueue提高并发性
*
* @author 马士兵
*/
package com.mashibing.juc.c_024_FromVectorToQueue;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class TicketSeller4 {
static Queue<String> tickets = new ConcurrentLinkedQueue<>();
static {
for(int i=0; i<1000; i++) tickets.add("票 编号:" + i);
}
public static void main(String[] args) {
for(int i=0; i<10; i++) {
new Thread(()->{
// 这里面没有加锁不会有问题
// 这里用的是ConcurrentLinkedQueue
// 因为poll是有锁的,他已经将值取出来了,而在线程中自己怎么去处理,怎么去打印是不影响的
// 取出来的值已经存到线程内部去了,所以什么时候打印,什么时候结束跟其他线程之间互相不影响,互相之间没关系
while(true) {
// 只要string在线程内部就不会有问题,但是放到外面作为成员属性就会有问题了
String s = tickets.poll();
// 下面不会阻塞
// 所以在这里加不加睡眠是没有任何关系的
if(s == null) break;
else System.out.println("销售了--" + s);
}
}).start();
}
}
}
面试:Queue和List的区别
- 添加了offer、poll以及put、take等这些对线程友好的或者阻塞或者延时等待的api
- Queue添加了很多对线程友好的api(offer、peek、poll)
- BlockingQueue在offer、peek、poll基础上又添加了put和take(主要实现了阻塞操作,可以自然而然地实现阻塞队列—->生产者消费者问题、模型)
- 生产者消费者模型是多线程中最重要地模型,是MQ(消息队列Message Queue)的基础,本质上就是大型的生产者消费者,面试必考必问!!!