目录
1.同步、异步、阻塞、非阻塞
同步和异步是消息通信的机制。
同步:发出一个请求即为一个同步调用。
(发出请求会得到响应,没有返回响应就会死等结果,需要注意在等待的过程中现成是激活的状态并没有暂停。)
异步:发出一个请求等待结果过程中,可以继续发出后续的请求。
(这个结果会通过callback、状态或者通知的方式来告知调用方结果。)
异步经历两个步骤:
1.调用方发起请求,
2.服务提供方对请求的结果进行返回。
单看一个请求的发起是同步操作,但是异步主要体现在只要有结果都会通过callback、状态或者通知的方式告知,因此不会存在同步的死等结果。
阻塞和非阻塞是程序在等待调用结果(消息,返回值)时的状态。
阻塞:调用结果返回之前,线程会挂起暂停运行,其他消息用户会进入等待。
非阻塞:调用结果未获取到,不会影响其他运行的线程,知道后面有结果了才会返回。
阻塞和非阻塞最大的区别就是看调用方线程是否会被挂起。
同步阻塞IO
发出请求,服务提供方未提供结果,线程挂起,直到拿到结果返回,这一个请求才算结束,第二个请求才能进来。
同步:call->retuen
阻塞:等待结果过程中,blocked线程挂起,不能处理别的事情。
如下图所示:
异步阻塞IO
发出第一个请求,服务提供方立刻返回,未拿到真正调用结果,但是该请求线程会被挂起,等待回调结果。期间可以后续请求,调用过程同样阻塞,直到回调对应结果,结束请求。
异步:call->retuen->callback
阻塞:等待结果过程中,blocked线程挂起,不能处理别的事情。
如下图所示:
同步非阻塞IO
发出请求,服务提供方立刻返回,没有回调结果,该请求继续call,一直不断地call,直到最总完成结果回调,需要注意在陆续call的过程中,线程未被挂起还是激活状态,可以处理其他事情,像立刻返回请求询问结果等
同步:call->retuen->call->retuen->ccall->callback
非阻塞:发出请求,线程激活状态,可以处理别的事情(立刻返回请求的询问结果)。
如下图所示:
异步非阻塞IO
请求发送出去以后,立刻返回,然后再等待该结果的callback,最后再次请求获取response,这整个过程是异步。
异步:发出请求后,可以继续发出请求。
非阻塞:Sender等待callback期间一直是可以处理其它事情的。
如下图所示:
2.java常见队列
ArrayList:可被用作队列,末尾添加和头部删除,但是arrayList是基于数组实现的,因此更适合查询,队列不做推荐。
LinkedList:可用作队列,实现了Deque,继承了Queue接口,LinkedList是基于双向链表实现,使用了指针来链接元素,而不需要移动其他元素。
ArrayBlockingQueue:有界阻塞队列,底层使用数组实现。它有一个固定的容量,并且在插入或删除元素时可能会阻塞线程,直到满足特定的条件。
LinkedBlockingQueue:有界或无界的阻塞队列,底层使用链表实现。
PriorityBlockingQueue:支持优先级的无界阻塞队列。
ConcurrentLinkedQueue:非阻塞无界队列,它适用于多线程环境。它使用链表实现,并且提供了高效的并发操作。
3.Queue
队列是一种特殊的线性表,遵循先入先出、后入后出的基本原则,一般来说,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
import java.util.LinkedList;
import java.util.Queue;
public class QueueTest {
// 定义队列
private static Queue<String> queue;
public static void main(String[] args) {
getQueue();
// testRemoveOrPoll();
testPeekOrElement();
}
// 队列中添加元素,返回布尔值
private static void getQueue() {
queue = new LinkedList<>();
// add和offer的区别:offer方法在队列已满时,不会抛出异常,而是返回false,
// 而add方法在队列已满时或添加失败时候,会抛出异常。
queue.offer("榴莲");
}
// 移除队列首个元素并返回元素
private static void testRemoveOrPoll(){
// poll和remove的区别:poll方法在队列为空时,返回null,
// 而remove方法在队列为空时,会抛出异常'NoSuchElementException'。
String remove = queue.remove();
System.out.println(remove);// 输出:榴莲
String poll = queue.poll();
System.out.println(poll);// 输出:null
}
// 检索返回队列的头元素,但不移除
private static void testPeekOrElement(){
// peek和element的区别:peek方法在队列为空时,返回null,而element方法在队列为空时,会抛出异常。
String peek = queue.element();// 输出:榴莲
System.out.println(peek);
String element = queue.element();// 输出:榴莲
System.out.println(element);
}
}
4.队列特性
队列主要分为阻塞和非阻塞,有界和无界、单向链表和双向链表
阻塞队列
入列(添加元素)时,超过队列总数,会进行等待(阻塞),待队列的中的元素出列后,元素数量未超过队列总数时,就会解除阻塞状态,进而可以继续入列;
出列(删除元素)时,队列为空的情况,也会进行等待(阻塞),待队列有值的时候即会解除阻塞状态,进而继续出列;
阻塞队列的好处是可以防止队列容器溢出;只要满了就会进行阻塞等待;也就不存在溢出的情况;只要是阻塞队列,都是线程安全的;
阻塞队列提供了以下2个方法:
出队阻塞方法 : take() // 添加一个元素,如果队列满,则阻塞
入队阻塞方法 : put() // 移除并返回队列头部的元素 如果队列为空,则阻塞
drainTo(list) //一次性取出队列所有元素
非阻塞队列
不管出列还是入列,都不会进行阻塞。
入列时,如果元素数量超过队列总数,则会抛出异常,
出列时,如果队列为空,则取出空值;
有界队列:有界限,大小长度受限制。
无界队列:无限大小,超过界限时就会进行内部动态扩容。
单向链表 : 每个元素中除了元素本身之外,还存储一个指针,这个指针指向下一个元素;
双向链表 :除了元素本身之外,还有两个指针,一个指针指向前一个元素的地址,另一个指针指向后一个元素的地址;