1. Queue中提供的一些多线程比较友好的接口(ConcurrentQueue的常用接口)
    1. offer—->相当于List的add,可以用offer的**返回值来判断是不是添加成功**==
    2. poll—->取数据
    3. peek—->拿数据
    4. 以上全是线程安全的操作
  2. 具体区别
    1. add加不进去了,会抛异常;而offer不管怎样都会给你一个返回值(true、false);现在都用offer
    2. peek去取不会remove掉
    3. poll去取并且remove掉
    4. 这几个操作在BlockingQueue中都是线程安全的操作(add是不是???)

BlockingQueue阻塞队列

  1. 提供了一系列的方法,可以实现线程自动阻塞
  2. BlockingQueue在Queue的基础上又添加了两个方法put、take;这两个方法真正实现了阻塞
    1. put表示一定要往里面装,如果满了,这个线程会阻塞住
    2. take表示往外取,如果空了的话,这个线程会阻塞住
    3. 即实现了生产者、消费者里面的那个队列容器,
    4. BlockingQueue天生就是对**多线程友好**的生产者、消费者模型,天生实现了生产者、消费者这个模型
  3. 阻塞的原理:和之前写的小程序没有太大的区别
    1. LockSupport的park方法—->是阻塞的概念,不是锁的概念—->进入一个wait状态,**不是锁**,只是阻塞当前线程
    2. wait、notify和await、signal
    3. ReentrantLock
    4. ……
  4. BlockingQueue提供了很多在多线程情况下友好的api
  5. put和take实现了阻塞,可以自然而然地实现任务队列,生产者消费者模型—->多线程中最重要的模型
  6. 5中的模型本质上是MQ(Message Queue)的基础,MQ本质上就是一个大型的生产者消费者模型(必考、必问)

LinkedBlockingQueue

  1. ❌链表实现的无界队列
  2. ❌可以向里面一直装,直到内存溢出,没有长度限制
  3. LinkedBlockingQueue不是无界队列,他的最大值是Integer的最大值说无界队列不精确
  4. LinkedBlockingQueue不能一直往里面扔直到内存崩了为止,超过最大值也就满了

ArrayBlockingQueue

  1. 有界的BlockingQueue
  2. 可以指定初始容器的容量大小
  3. 一旦满了之后程序就会阻塞住,阻塞在put处,等待消费者消费
  4. 在满了的情况下用add会报异常
  5. 不管满不满offer都会及时返回,成功返回true,失败返回false;用返回时来判断有没有加成功
  6. 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 strs = new SynchronousQueue<>();

  1. new Thread(()->{
  2. try {
  3. System.out.println(strs.take());
  4. } catch (InterruptedException e) {
  5. e.printStackTrace();
  6. }
  7. }).start();
  8. strs.put("aaa"); //阻塞等待消费者消费
  9. //strs.put("bbb");
  10. //strs.add("aaa");
  11. System.out.println(strs.size());
  12. }

}

  1. <a name="Mllak"></a>
  2. ## TransferQueue
  3. > 1. 是前面几种Queue的组合,但是又有些区别
  4. > 1. 可以给线程传递任务,但不止能传递一个任务(SynchronousQueue只能传递一个任务)
  5. > 1. TransferQueue中是一个链表,可以传好多个
  6. > 1. transfer方法装完,就阻塞在那里,直到有人将其取走,他才会继续干下面的事情(装完取走付钱,必须付钱之后才能取走),transfer不满也会等着,来了就等着
  7. > 1. 底层有一个比较好玩的机制:消费者没有东西消费的时候放空蓝子???等着生产者往里面装
  8. > 1. SynchronousQueue中没有空篮子,因为他是线程之间手递手传递
  9. > 1. TransferQueue是多人对多人的手递手传递
  10. ```java
  11. package com.mashibing.juc.c_025;
  12. import java.util.concurrent.LinkedTransferQueue;
  13. public class T09_TransferQueue {
  14. public static void main(String[] args) throws InterruptedException {
  15. LinkedTransferQueue<String> strs = new LinkedTransferQueue<>();
  16. // 必须先有消费者去获取,否则直接上生产者的话会造成阻塞在那里,不能往下执行
  17. new Thread(() -> {
  18. try {
  19. System.out.println(strs.take());
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }).start();
  24. strs.transfer("aaa");
  25. //strs.put("aaa");
  26. /*new Thread(() -> {
  27. try {
  28. System.out.println(strs.take());
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }).start();*/
  33. }
  34. }

ConcurrentLinkedQueue(和LinkedBlockingQueue区别)

  1. Concurrent与Blocking一般不连用
    1. 并发就是并发,直接就可以了
    2. 阻塞就是阻塞,不是并发
  2. 可以采用这种简单的不加锁的方式,效率会高上很多
  1. /**
  2. * 有N张火车票,每张票都有一个编号
  3. * 同时有10个窗口对外售票
  4. * 请写一个模拟程序
  5. *
  6. * 分析下面的程序可能会产生哪些问题?
  7. * 重复销售?超量销售?
  8. *
  9. * 使用Vector或者Collections.synchronizedXXX
  10. * 分析一下,这样能解决问题吗?
  11. *
  12. * 就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
  13. * 就像这个程序,判断size和进行remove必须是一整个的原子操作
  14. *
  15. * 使用ConcurrentQueue提高并发性
  16. *
  17. * @author 马士兵
  18. */
  19. package com.mashibing.juc.c_024_FromVectorToQueue;
  20. import java.util.Queue;
  21. import java.util.concurrent.ConcurrentLinkedQueue;
  22. public class TicketSeller4 {
  23. static Queue<String> tickets = new ConcurrentLinkedQueue<>();
  24. static {
  25. for(int i=0; i<1000; i++) tickets.add("票 编号:" + i);
  26. }
  27. public static void main(String[] args) {
  28. for(int i=0; i<10; i++) {
  29. new Thread(()->{
  30. // 这里面没有加锁不会有问题
  31. // 这里用的是ConcurrentLinkedQueue
  32. // 因为poll是有锁的,他已经将值取出来了,而在线程中自己怎么去处理,怎么去打印是不影响的
  33. // 取出来的值已经存到线程内部去了,所以什么时候打印,什么时候结束跟其他线程之间互相不影响,互相之间没关系
  34. while(true) {
  35. // 只要string在线程内部就不会有问题,但是放到外面作为成员属性就会有问题了
  36. String s = tickets.poll();
  37. // 下面不会阻塞
  38. // 所以在这里加不加睡眠是没有任何关系的
  39. if(s == null) break;
  40. else System.out.println("销售了--" + s);
  41. }
  42. }).start();
  43. }
  44. }
  45. }

面试:Queue和List的区别

  1. 添加了offer、poll以及put、take等这些对线程友好的或者阻塞或者延时等待的api
  2. Queue添加了很多对线程友好的api(offer、peek、poll)
  3. BlockingQueue在offer、peek、poll基础上又添加了put和take(主要实现了阻塞操作,可以自然而然地实现阻塞队列—->生产者消费者问题、模型)
  4. 生产者消费者模型是多线程中最重要地模型,是MQ(消息队列Message Queue)的基础,本质上就是大型的生产者消费者,面试必考必问!!!

fail-fast会发出ConcurrentModificationException的异常

阻塞和锁是不一样的,阻塞是wait(等待别人),而锁是为了确保原子性和可见性的一个操作

await这些的底层用的也是park阻塞,并不冲突

add满了的情况下会抛异常(如下)(offer不会异常,put会阻塞)

image.png

非替代关系,而是各有各的用途