JAVA中IO流,多线程,集合,JVM⾯试题汇总
JAVA中 IO流,多线程,集合,JVM ⾯试题汇总
IO流
java 中 IO 流分为⼏种?
按照流的流向分,可以分为输⼊流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的⾓⾊划分为节点流和处理流。
Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派⽣出来的。
InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作⽅式分类结构图:
按操作对象分类结构图:
BIO,NIO,AIO 有什么区别?
简答
BIO:Block IO 同步阻塞式 IO,就是我们平常使⽤的传统 IO,它的特点是模式简单使⽤⽅便,并发处理能⼒低。NIO:Non IO 同步⾮阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复⽤。AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步⾮堵塞 IO ,异步 IO 的操作基于事件和回调机制。
详细回答
BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写⼊必须阻塞在⼀个线程内等待其完成。在活动连接数不是特别⾼(⼩于单机1000)的情况下,这种模型是⽐较不错的,可以让每⼀个连接专注于⾃⼰的 I/O 并且编程模型简单,也不⽤过多考虑系统的过载、限流等问题。线程池本⾝就是⼀个天然的漏⽃,可以缓冲⼀些系统处理不了的连接或请求。但是,当⾯对⼗万甚⾄百万级连接的时候,传统的 BIO 模型是⽆能为⼒的。因此,我们需要⼀种更⾼效的 I/O 处理模型来应对更⾼的并发量。
NIO (New I/O): NIO是⼀种同步⾮阻塞的I/O模型,在Java 1.4 中引⼊了NIO框架,对应 java.nio 包,提供了 Channel ,
Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它⽀持⾯向缓冲的,基于通道的I/O操作⽅法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都⽀持阻塞和⾮阻塞两种模式。阻塞模式使⽤就像传统中的⽀持⼀样,⽐较简单,但是性能和可靠性都不好;⾮阻塞模式正好与之相反。对于低负载、低并发的应⽤程序,可以使⽤同步阻塞I/O来提升开发速率和更好的维护性;对于⾼负载、⾼并发的(⽹络)应⽤,应使⽤ NIO 的⾮阻塞模式来开发
AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改进版 NIO 2,它是异步⾮阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在那⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。AIO 是异步IO的缩写,虽然 NIO 在⽹络操作中,提供了⾮阻塞的⽅法,但是 NIO 的 IO ⾏为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程⾃⾏进⾏ IO 操作,IO操作本⾝是同步的。查阅⽹上相关资料,我发现就⽬前来说AIO 的应⽤还不是很⼴泛,Netty 之前也尝试使⽤过 AIO,不过⼜放弃了。
Files的常⽤⽅法都有哪些?
Files. exists():检测⽂件路径是否存在。
一个线程可以包含多个进程Files. createFile():创建⽂件。
Files. createDirectory():创建⽂件夹。
Files. delete():删除⼀个⽂件或⽬录。
Files. copy():复制⽂件。
Files. move():移动⽂件。
Files. size():查看⽂件个数。
Files. read():读取⽂件。
Files. write():写⼊⽂件。
多线程
为什么要使⽤并发编程(并发编程的优点)
充分利⽤多核CPU的计算能⼒:通过并发编程的形式可以将多核CPU的计算能⼒发挥到极致,性能得到提升
⽅便进⾏业务拆分,提升系统并发能⼒和性能:在特殊的业务场景下,先天的就适合于并发编程。现在的系统动不动就要求百万级甚⾄千万级的并发量,⽽多线程并发编程正是开发⾼并发系统的基础,利⽤好多线程机制可以⼤⼤提⾼系统整体的并发能⼒以及性能。⾯对复杂业务模型,并⾏程序会⽐串⾏程序更适应业务需求,⽽并发编程更能吻合这种业务拆分 。
什么是多线程,多线程的优劣?
多线程:多线程是指程序中包含多个执⾏流,即在⼀个程序中可以同时运⾏多个不同的线程来执⾏不同的任务。
1. 多线程的好处:
可以提⾼ CPU 的利⽤率。在多线程程序中,⼀个线程必须等待的时候,CPU 可以运⾏其它的线程⽽不是等待,这样就⼤⼤提⾼了程序的效率。也就是说允许单个程序创建多个并⾏执⾏的线程来完成各⾃的任务。
2. 多线程的劣势:
线程也是程序,所以线程需要占⽤内存,线程越多占⽤内存也越多;
多线程需要协调和管理,所以需要 CPU 时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞⽤共享资源的问题。
进程与线程的区别
线程具有许多传统进程所具有的特征,故⼜称为轻型进程(Light—Weight Process)或进程元;⽽把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有⼀个线程的任务。在引⼊了线程的操作系统中,通常⼀个进程都有若⼲个线程,⾄少包含⼀个线程。
根本区别:进程是操作系统资源分配的基本单位,⽽线程是处理器任务调度和执⾏的基本单位
资源开销:每个进程都有独⽴的代码和数据空间(程序上下⽂),程序之间的切换会有较⼤的开销;线程可以看做轻量级的进程,同⼀类线程共享代码和数据空间,每个线程都有⾃⼰独⽴的运⾏栈和程序计数器(PC),线程之间切换的开销⼩。
包含关系:如果⼀个进程内有多个线程,则执⾏过程不是⼀条线的,⽽是多条线(线程)共同完成的;线程是进程的⼀部分,所以线程也被称为轻权进程或者轻量级进程。
内存分配:同⼀进程的线程共享本进程的地址空间和资源,⽽进程之间的地址空间和资源是相互独⽴的
影响关系:⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。所以多进程要⽐多线程健壮。
执⾏过程:每个独⽴的进程有程序运⾏的⼊⼝、顺序执⾏序列和程序出⼝。但是线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制,两者均可并发执⾏
创建线程有哪⼏种⽅式?
创建线程有四种⽅式:
继承 Thread 类;
1. 定义⼀个Thread类的⼦类,重写run⽅法,将相关逻辑实现,run()⽅法就是线程要执⾏的业务逻辑⽅法
2. 创建⾃定义的线程⼦类对象
3. 调⽤⼦类实例的star()⽅法来启动线程
实现 Runnable 接⼝;
1. 定义Runnable接⼝实现类MyRunnable,并重写run()⽅法
2. 创建MyRunnable实例myRunnable,以myRunnable作为参数创建Thead对象,该Thread对象才是真正的线程对象
3. 调⽤线程对象的start()⽅法
实现 Callable 接⼝;
1. 创建实现Callable接⼝的类myCallable
2. 以myCallable为参数创建FutureTask对象
3. 将FutureTask作为参数创建Thread对象
4. 调⽤线程对象的start()⽅法
使⽤ Executors ⼯具类创建线程池
Executors提供了⼀系列⼯⼚⽅法⽤于创先线程池,返回的线程池都实现了ExecutorService接⼝。
说说线程的⽣命周期及五种基本状态?
1. 新建(new):新创建了⼀个线程对象。
2. 可运⾏(runnable):线程对象创建后,当调⽤线程对象的 start()⽅法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使⽤
权。
3. 运⾏(running):可运⾏状态(runnable)的线程获得了cpu时间⽚(timeslice),执⾏程序代码。注:就绪状态是进⼊到运⾏状态的唯
⼀⼊⼝,也就是说,线程要想进⼊运⾏状态执⾏,⾸先必须处于就绪状态中;
4. 阻塞(block):处于运⾏状态中的线程由于某种原因,暂时放弃对 CPU的使⽤权,停⽌执⾏,此时进⼊阻塞状态,直到其进⼊到就绪
状态,才 有机会再次被 CPU 调⽤以进⼊到运⾏状态。
阻塞的情况分三种:
(⼀). 等待阻塞:运⾏状态中的线程执⾏ wait()⽅法,JVM会把该线程放⼊等待队列(waitting queue)中,使本线程进⼊到等待阻塞状态;
(⼆). 同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占⽤),,则JVM会把该线程放⼊锁池(lock pool)中,线程会进⼊同步阻塞状态;
(三). 其他阻塞: 通过调⽤线程的 sleep()或 join()或发出了 I/O 请求时,线程会进⼊到阻塞状态。当 sleep()状态超时、join()等待线程
终⽌或者超时、或者 I/O 处理完毕时,线程重新转⼊就绪状态。
5. 死亡(dead):线程run()、main()⽅法执⾏结束,或者因异常退出了run()⽅法,则该线程结束⽣命周期。死亡的线程不可再次复⽣。
synchronized 的作⽤?
在 Java 中,synchronized 关键字是⽤来控制线程同步的,就是在多线程的环境下,控制 synchronized 代码段不被多个线程同时执⾏。synchronized 可以修饰类、⽅法、变量。
另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex
Lock 来实现的,Java 的线程是映射到操作系统的原⽣线程之上的。如果要挂起或者唤醒⼀个线程,都需要操作系统帮忙完成,⽽操作系统实现线程之间的切换时需要从⽤户态转换到内核态,这个状态之间的转换需要相对⽐较长的时间,时间成本相对较⾼,这也是为什么早期的synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官⽅对从 JVM 层⾯对synchronized 较⼤优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引⼊了⼤量的优化,如⾃旋锁、适应性⾃旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
synchronized 和 volatile 的区别是什么?
synchronized 表⽰只有⼀个线程可以获取作⽤对象的锁,执⾏代码,阻塞其他线程。
volatile 表⽰变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁⽌指令重排序。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论