Java基础技术-Java其他主题【面试】

Java基础技术IO与队列

Java BIO、NIO、AIO

Java 中 BIO、NIO、AIO 的区别是什么?

含义不同:

BIO(Blocking IO)是同步并阻塞的 IO,线程发起 IO 请求后,不论内核是否准备好 IO 操作,都会一直阻塞直到操作完成

NIO(Non-blocking IO)是同步非阻塞的 IO,线程发起 IO 请求后立即返回;内核在做好 IO 操作的准备之后,通过调用注册的回调函数通知线程做 IO 操作,线程开始阻塞,直到操作完成

AIO(Asynchronous IO)是异步非阻塞的 IO,线程发起 IO 请求,立即返回;内存做好 IO 操作的准备之后,做 IO 操作,直到操作完成或者失败,通过调用注册的回调函数通知线程做 IO 操作完成或者失败

应用场景不同:

BIO 从 JDK1.4 之前的版本,适用于低负载、低并发、业务逻辑耗时较长的场景

NIO 从 JDK1.4 开始支持,适用于高负载高并发且业务逻辑简单(轻操作)的场景,典型场景是聊天服务器

AIO 从 JDK1.7 开始支持,适用于高负载高并发且业务逻辑复杂(重操作)的场景,典型场景是相册服务器

Java 中三者的关系

从发展历史来看,BIO->NIO->AIO,后者的出现是为了扩展前者的应用场景

BIO:在服务端,通常是在 while 循环中调用 accept 方法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成

如果 BIO 要能够同时处理多个客户端请求,就必须使用多线程,即每次 accept 阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续 accept 等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理

NIO:与 BIO 的最大的区别是多路复用的思想,只需要开启一个线程就(或者少量多线程)可以处理来自多个客户端的 IO 事件,用来扩展 BIO 的高并发场景

若服务端监听到客户端连接请求,便为其建立通信套接字 (java 中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字

若服务端监听到来自已经创建了通信套接字的客户端发送来的数据,就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理

AIO:与 NIO 不同,当进行读写操作时,只须直接调用 API 的 read 或 write 方法即可,这两种方法均为异步的,完成后会主动调用回调函数

如果是读操作,操作系统会将可读的流传入 read 方法的缓冲区,并通知应用程序,如果是写操作,操作系统在将 write 方法传递的流写入完成后,也会通知应用程序

在 JDK1.7 中,在 channels 包下增加了四个异步通道来实现:AsynchronousSocketChannel,AsynchronousServerSocketChannel,AsynchronousFileChannel,AsynchronousDatagramChannel,这四个类的 read/write 方法,都会返回一个带回调函数的对象,当执行完读取 / 写入操作后,直接调用回调函数

同步与异步、阻塞与非阻塞的区别是什么?

同步与异步:

同步:调用线程发出同步请求后,在没有得到结果前,该调用就不会返回,前面的同步调用处理完了后才能处理下一个同步调用

异步:调用线程发出异步请求后,在没有得到结果前,该调用就返回了,真正的结果数据会在业务处理完成后以信号或者回调的形式通知调用者

阻塞与非阻塞:

阻塞:调用线程发出请求后,在没有得到结果前,该线程就会被挂起,此时该线程处于非可执行状态,直到返回结果返回后,此线程才会被唤醒,继续运行

非阻塞:调用线程发出请求后,在没有得到结果前,该调用就返回了,整个过程该线程不会被挂起

简单地说:同步和异步的关键差异在于 IO 操作就绪后有没有回调,是针对数据处理端而言,而在同步的时候 ,IO 操作是否就绪后没有回调,通常需要业务发起端使用轮询方法查看,如果是异步的话 ,那IO 操作是否就绪后有回调,通常会主动唤起业务发起端

阻塞和非阻塞的关键差异在于 IO 操作尚未就绪时,进程是否需要等待,是针对业务发起端而言,在阻塞的情况下,要是IO 操作沿未就绪时,业务发起端的用户程序就进入等待状态,直到数据准备好或者可以读写为止,而在非阻塞的情况时,如果 IO 操作沿未就绪时,那业务发起端的用户程序可以立刻返回无须等待

比如说,我煮米饭,有一个普通的蒸锅,还有一个电饭煲,我拿着蒸锅去炉灶上蒸米饭,然后在边上等米饭出锅,这就是同步阻塞

我要是把蒸锅放在炉灶上蒸米饭,然后不在边儿上等着,我去打五把CSGO,中间时不时过来看看能不能出锅,这就是同步非阻塞

这次换个锅,用电饭煲,一样的,等着米饭出锅,不过电饭煲会在米饭熟的时候自己叮一下,这次我还是在边儿上等着出锅,这就是异步阻塞

然后下一次我觉得我在边上干等着,有点儿没意思,就做好准备以后,去打五把CSGO去了,电饭煲不叮我就不过来看,这就是异步非阻塞

对于炊具来说,蒸锅就是同步,而电饭煲就是异步的,蒸锅只能让我(调用者)来轮询能不能出锅,而电饭煲是饭熟了就回自己响,提示我饭熟了,至于阻塞非阻塞来说,我在边上待着不动就是阻塞,放着不管去打CSGO就是非阻塞

字节流、字符流的区别及适用场景分别是什么?

区别:

处理单元不同,J 字节流处理的最基本单位为 1 个字节,字符流处理的最基本的单元是 Unicode 代码单元(大小 2 字节)

字节流默认不使用缓冲区;字符流使用缓冲区

适用场景:

字节流实际上可以处理任何文件,因为字节是存储的基础单元,而待处理的流如果是可打印的字符,那么用字符流更方便一些

什么是缓冲区?有什么作用呢?

可以把缓冲区理解为一段特殊的内存,字符流操作时为了不那么频繁地读写 IO,会把一部分数据暂时读入到内存的某块区域,然后直接从该区域读取数据,以提升程序的性能

在字符流的操作中,所有的字符都是在内存中形成的,从而使用缓冲区暂存数据,如果想在不关闭时也可以将字符流的内容全部输出,则可以使用 Writer 类中的 flush () 方法完成

栈(Stack)和队列(Queue)的异同?

相同点:

栈和队列都是属于线性表

栈和队列插入操作都是限定在线性表的头尾进行

栈和队列插入与删除的时间复杂度都是 O (1)

不同点:

特性不同,栈后进先出(LIFO,Last In First Out),队列先进先出(FIFO,First In First Out)

栈只在表的一端进行插入和删除操作,队列只在表的一端进行插入操作,在表的另一端进行删除操作

JAVA 中的栈 (Stack) 继承自 Vector,再往上的接口是 List/Collection,而队列(Queue) 直接继承的是 Collection 接口

如何用两个栈实现队列(入队和出队)?

代码如下:

public class StackQueue<E> {
private Stack<E> stack1 =new Stack<>();
private Stack<E> stack2 =new Stack<>(); public void push(E element) {
stack1.add(element);
} public E poll() {
if (stack2.isEmpty()) {
while (stack1.size() > 0) {
stack2.add(stack1.pop());
}
}
if (stack2.isEmpty()) {
throw new RuntimeException("queue is Empty!");
}
E head = stack2.pop();
return head;
} public static void main(String[] args) {
StackQueue<String> stackQueue = new StackQueue();
stackQueue.push("first");
stackQueue.push("second");
}
}

实现过程简单地说,就是把数据先压入 stack1,然后再从 stack1 中取出压入 stack2(后进先出),取数的时候直接从 stack2 出(后进先出),经过两次后进先出就符合队列先进先出的特性了,其中stack1 用于存储元素,stack2 用于弹出元素

ArrayBlockingQueue 和 LinkedBlockingQueue 的区别是什么?

ArrayBlockingQueue 和 LinkedBlockingQueue 都是阻塞队列(BlockingQueue)

区别:

内部存储结构不同,ArrayBlockingQueue 采用的是数组存储,而 LinkedBlockingQueue 采用的是 Node 节点

ArrayBlockingQueue 初始化时必须指定容量值,LinkedBlockingQueue 可以不用指定(默认容量为 Integer.MAX_VALUE)

Queue 及其常用子类可分为几类?

大致可分为三类:

双端队列(Deque):头部和尾部都支持元素插入和获取

阻塞队列(BlockingQueue):在元素的添加 / 删除操作时,如果没有成功,会阻塞等待执行,例如,当添加元素时,如果队列元素已满,队列会阻塞等待直到有空位时再插入

非阻塞队列(Non BlockingQueue):在元素的添加 / 删除操作时,如果没有成功,会直接返回操作的结果(通常双端队列也属于非阻塞队列)

Queue 的常见特性PriorityBlockingQueue,DelayQueue,SynchronousQueue?

PriorityBlockingQueue:带优先级的无界阻塞队列,元素按优先级顺序被移除,而不是先进先出队列(FIFO)

此外,队列中的元素要具有比较能力(用于判定优先级),PriorityBlockingQueue 不允许 null 元素

DelayQueue:存放 Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素

该队列的头部是延迟期满后保存时间最长的 Delayed 元素,如果延迟都还没有期满,则队列没有头部,DelayQueue 不允许使用 null 元素

SynchronousQueue:同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然

它的一个典型应用场景是 Executors.newCachedThreadPool () ,在新任务到来时创建新的线程,如果有空闲线程则会重复使用,SynchronousQueue 不允许 null 元素

Java基础技术-Java其他主题【面试】的更多相关文章

  1. Java基础技术多线程与并发面试【笔记】

    Java基础技术多线程与并发 什么是线程死锁? ​死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...

  2. Java基础技术基础面试【笔记】

    Java基础技术基础面试[笔记] String.StringBuilder以及StringBuffer三者之间的区别? 三者的区别可以从可变性,线程安全性,性能这三个部分进行说明 可变性 从可变性来说 ...

  3. Java基础技术JVM面试【笔记】

    Java基础技术JVM面试[笔记] JVM JVM 对 java 类的使用总体上可以分为两部分:一是把静态的 class 文件加载到 JVM 内存,二是在 JVM 内存中进行 Java 类的生命周期管 ...

  4. java基础技术集合面试【笔记】

    java基础技术集合面试[笔记] Hashmap: 基于哈希表的 Map 接口的实现,此实现提供所有可选的映射操作,并允许使用 null 值和 null 键(除了不同步和允许使用 null 之外,Ha ...

  5. java基础(二)-----java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  6. 黑马程序员:Java基础总结----java注解

    黑马程序员:Java基础总结 java注解   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! java注解 lang包中的基本注解 @SuppressWarnings ...

  7. Java基础:Java的四种引用

    在Java基础:java虚拟机(JVM)中,我们提到了Java的四种引用.包括:强引用,软引用,弱引用,虚引用.这篇博客将详细的讲解一下这四种引用. 1. 强引用 2. 软引用 3. 弱引用 4. 虚 ...

  8. java基础-学java util类库总结

    JAVA基础 Util包介绍 学Java基础的工具类库java.util包.在这个包中,Java提供了一些实用的方法和数据结构.本章介绍Java的实用工具类库java.util包.在这个包中,Java ...

  9. Java基础-使用JAVA代码剖析MD5算法实现过程

    Java基础-使用JAVA代码剖析MD5算法实现过程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

随机推荐

  1. Spring源码编译一次性通过&遇到的坑解决方法

    前言 spring源码本地编译,按网上的博客参考资料的操作步骤,总是会出现各种莫名其妙的错误.根据错误信息找解决方案,但在自己的环境下又总是编译不过去.结合参加培训学习Jack老师提供的方法,自己多种 ...

  2. docker下创建redis cluster集群

    概述 在Redis中,集群的解决方案有三种 主从复制 哨兵机制 Cluster Redis Cluster是Redis的分布式解决方案,在 3.0 版本正式推出. 准备工作 1.确定本机IP地址 2. ...

  3. fail-fast 与 fail-safe

    fail-fast: fail-fast(快速失败),是Java集合的一种错误检测机制.当在遍历集合的过程中该集合在结构(改变集合大小)上发生变化时候,有可能发生fail-fast(快速失败行为不能得 ...

  4. XCTF crypto 不仅仅是Mors

    一. 题目暗示摩斯码,打开文件发现里面有反斜杠的.不管它直接拿来解密 二. 发现一句话是句英文,还有其他的加密方式,后面那串只有两种字符A和B,手抓饼A套餐,b套餐 培根加密,拿来解密后,得到flag

  5. 一款不错的 Go Server/API boilerplate,使用 K8S+DDD+CQRS+ES+gRPC 最佳实践构建

    Golang API Starter Kit 该项目的主要目的是使用最佳实践.DDD.CQRS.ES.gRPC 提供样板项目设置. 为开发和生产环境提供 kubernetes 配置.允许与反映生产的 ...

  6. 「AGC029C」Lexicographic constraints

    「AGC029C」Lexicographic constraints 传送门 好像这个题非常 easy. 首先这个答案显然具有可二分性,所以问题转化为如何判定给定的 \(k\) 是否可行. 如果 \( ...

  7. Java 给PDF签名时添加可信时间戳

    一.程序运行环境 编译环境:IntelliJ IDEA 所需测试文件:PDF..pfx数字证书及密钥.PDF Jar包(Free Spire.PDF for Java).签名图片(.png格式) 可信 ...

  8. python得到当前版本号

    import sys print(sys.winver) 3.7 # 导入sys模块的argv,winver成员,并为其指定别名v.wv from sys import argv as v, winv ...

  9. 在 Golang 中实现一个简单的Http中间件

    本文主要针对Golang的内置库 net/http 做了简单的扩展,通过添加中间件的形式实现了管道(Pipeline)模式,这样的好处是各模块之间是低耦合的,符合单一职责原则,可以很灵活的通过中间件的 ...

  10. 如何移除本地文件夹与Git的连接

    1.在需要移除的文件夹下打开Git Bash 2.在命令行中输入如下语句 find . -name ".git" | xargs rm -Rf