58.大数据之旅——电信日志项目01

本文深入探讨了Java的BIO和NIO机制,从进程和线程的概念出发,详细解释了为何引入线程和进程,以及它们在多道编程中的作用。接着,介绍了Java NIO的原理和应用场景,强调了NIO在高并发场景下的优势,并通过代码示例展示了BIO中产生阻塞的4种方法。此外,还讨论了Buffer、Channel、Selector等核心概念,并分析了NIO如何解决粘包问题。最后,文章通过一系列试题回顾了NIO和Hadoop的相关知识,帮助读者巩固理解。
摘要由CSDN通过智能技术生成

进程和线程


什么是进程?
进程就是进展中的程序,或者说是执行中的程序。一个程序加载到内存后,就变为进程。
即:进程=程序+执行

进程模型的三个视角
在这里插入图片描述
为什么要引入进程机制?
原因是为了实现多道编程。
多道编程的好处一是提高cpu的利用率。
举例:如果一个程序,有20%的使用cpu进行计算,另外80%的时间用来进行I/O
如果是单道编程,cpu的利用率是1-0.8=0.2。
如果是多道编程,比如两个进程,cpu的利用率是1-0.80.8=0.36 (两个进程都处于I/O时,cpu才会处于空闲)。
如果是三个进程,1-0.8
0.8*0.8=48%。即随着进程数量增多,cpu利用率上升,并趋近于某个阈值。

多道编程的好处二是响应时间改善

进程的产生和销毁
在这里插入图片描述
后来人们根据以上状态,总结出进程的三种状态:
1.执行
2.就绪
3.阻塞
CPU只轮询和运算前两种状态进程,不会轮询阻塞态的进程。此外,三种状态之间可以进行各种转换。但是标红的转换不可能发生。
执行——就绪
执行——阻塞
就绪——阻塞
就绪——执行
阻塞——执行
阻塞——就绪

什么是线程
为什么要有线程?就拿我用onenote写课堂笔记来说,onenote需要监听我打字输入的状态,还需要每隔一段时间对笔记进行自动保存。假设如果只有这个进程本身,那么当对笔记进行保存时,因为此时需要访问硬盘,而访问硬盘的时间进程是阻塞状态的,这时我的任何输入都会没有响应,这种用户体验是无法接受的,或许我们可以通过键盘或者鼠标的输入去中断保存草稿的过程,但这种方案也并不讨好。而引入了线程概念后,确切的说,我们用一个线程或多个线程来完成工作,每个线程仅仅需要处理自己那一部分应该完成的任务,而不用去关心和其它线程的冲突。

操作系统引入线程概念后,线程是进程真正工作的单位,一个进程下至少有一个线程。线程也是进程的分身,能够让一个进程在宏观上同时干多件事。

因为线程是进程的分身,所以每个线程在本质上是一样的,有相同的程序文本。线程和进程一样,有三种状态:执行、就绪和阻塞。

总结:进程的出现,是为了满足多道编程,提高cpu利用率。而线程的出现,是为了让一个进程在宏观上同时做多件事。
即现代计算机引入了进程和线程后,使得cpu利用率和操作响应时间大大提高。
进程是组织资源的最小单位,独占一块内存地址空间,在内存空间里存储一些全局变量等。
线程是cpu执行的最小单位,即程序在工作,实际是cpu在运算一个个的线程。
打个比喻:进程一个舞台,组织了各种资源,灯光、道具等。而线程就是这舞台上的演员,共享和运用舞台的资源来表演。

NIO与BIO


NIO概述
Non-Blocking I/O,是一种非阻塞通信模型。不同的语言或操作系统都有其不同的实现。
我们主要学习基于Java语言的NIO,也称为java.nio。
java.nio是jdk1.4版本引入的一套API,我们可以利用这套API实现非阻塞的网络编程模型。

为什么要学习NIO
目前无论是何种应用,都是分布式架构,因为分布式架构能够抗高并发,实现高可用,负载均衡以及存储和处理海量数据,而分布式架构的基础是网络通信。因此, 因此,网络编程始终是分布式软件工程师和架构师的必备高端基础技能之一。

随着当前大数据和实时计算技术的兴起,高性能 RPC 框架与网络编程技术再次成为焦点。 比如Fackebook的Thrift框架,scala的 Akka框架,实时流领域的 Storm、Spark框架,又或者开源分布式数据库中的 Mycat、VoltDB,这些框架的底层通信技术都采用了 NIO(非阻塞通信)通信技术。而 Java 领域里大名鼎鼎的 NIO 框架——Netty,则被众多的开源项目或商业软件所采用。

java.bio和java.nio的区别
在这里插入图片描述
NIO和BIO的应用场景
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,网页浏览。编程比较复杂,

BIO开发,处理每一个客户端都需要在服务器端创建一个线程来处理。开启线程需要占用内存资源,并且线程轮询也需要消耗资源。所以,BIO编程,不可能应对高用户量访问的场景。
但是有些持续性操作,比如上传下载一个大文件,用NIO的方式不会比IO好。

BIO实现的Socket——4种产生阻塞的方法


BIO 4种产生阻塞的方法:

ServerDemo1代码(用于测试accpet,connect,read):

/**
 * 这个类是作为socket的服务端
 * @author ysq
 *
 */
public class ServerDemo1 {
   
 
public static void main(String[] args) throws Exception {
   
ServerSocket ss=new ServerSocket();
ss.bind(new InetSocketAddress(9999));
 //accept方法会产生阻塞,直到有客户端连接
 //传统的BIO会产生阻塞:
 //1.服务端accept()方法会产生阻塞
Socket s=ss.accept();
 
//接下来做读入流的测试
//当有客户端接入时,accpet()方法不阻塞
//但是客户端没有任何的流输入,所以产生了阻塞
//2.即read()方法也会产生阻塞
InputStream in=s.getInputStream();
System.out.println("1");
in.read();
System.out.println("2");
 
}
 
}

ClientDemo1代码(用于测试accpet,connect,read):

/**
 * 这个类是客户端的Socket
 * @author ysq
 *
 */
public class ClientDemo1 {
   
 
public static void main(String[] args) throws Exception {
   
//如果服务端未启动,客户端就连接的话会报错,Connection refused
//但是,需要留意的是,这个异常在程序启动后,并不是马上抛出的
//而是卡顿了一秒钟才出现的
//这个现象的原因:
//客户端程序启动=》尝试连接服务端=》等待服务端的链接响应=》服务端没有启动=》
//客户端收到服务端的响应,报出错误提示
//实际上,对应客户端,socket.connect()这个方法也会产生阻塞
Socket socket=new Socket();
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
 
//while(true){}的意思是让客户端一直保持连接。
while(true){
   }
 
 
 
 
}
}

ServerDemo2代码(用于测试write方法):

public class ServerDemo2 {
   
 
public static void main(String[] args) throws Exception {
   
ServerSocket ss =new ServerSocket();
ss.bind(new InetSocketAddress(9998));
 
Socket s=ss.accept();
 
while(true){
   
 
}
 
}
}

ClientDemo2代码(用于测试write方法):

/**
 *这个类是用来测试客户端socket write()方法是否阻塞
 */
public class ClientDemo2 {
   
 
public static void main(String[] args) throws IOException {
   
Socket s=new Socket();
s.connect(new InetSocketAddress("127.0.0.1",9998));
 
OutputStream out=s.getOutputStream();
for(int i=0;i<1000000;i++){
   
System.out.println(i);
//结果证明,当不断向outputStream里写数据时,写到一定大小后,会产生阻塞
//即Write方法也会产生阻塞
out.write("a".getBytes());
}
out.flush();
out.close();
}
}
 

由于底层设备的缓存大小有限,当一直向缓冲区里写数据,却一直不往出取时,达到缓冲区大小上限时,就会造成阻塞,写不出去了。

Buffer—ByteBuffer


Buffer:缓冲区,内存里一段连续的空间
Buffer的子类,对应了8种基本数据类型里的七种(没有boolean类型),分别是:
1.ByteBuffer
2.CharBuffer
3.DoubleBuffer
4.FolatBuffer
5.IntBuffer
6.LongBuffer
7.ShortBuffer
在这里插入图片描述
ByteBuffer
1.创建缓冲区

static  ByteBuffer allocate(int capacity)
参数:capacity 缓冲区的容量,以字节为单位 
相关代码:
@Test
public void testCreateBuffer(){
   
//ByteBuffer是一个抽象类,我们得到的是他的实现子类对象,HeapByteBuffer
ByteBuffer buffer=ByteBuffer.allocate(1024);
 
 
}

相关代码:

@Test
public void testCreateBufferByWrap(){
   
//wrap方法也可以创建一个Buffer,接收的是一个字节数组,
//并且利用wrap方法创建完buffer之后,buffer里就有了字节数组里的数据
byte[] b={
   1,2,3,4};
ByteBuffer buffer=ByteBuffer.wrap(b);
//也可以通过指定数组下标的界限来创建Buffer并填充数据,需要注意的是:
//利用此方法创建buffer,buffer的容量和数组的容量一致,buffer里也包含了数组的全部数据,
//不同的是limit的位置变化了
ByteBuffer buffer2=ByteBuffer.wrap(b,0,2);
}
 

2.向缓冲区里写数据
put(byte b)
put(byte[] src)
putXxx()
代码:

public void testPutData(){
   
 
ByteBuffer buffer=ByteBuffer.allocate(1024);
//利用put方法,存入的是字节数据,占一个字节
buffer.put
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值