在这篇文章中,不仅会讲到Java流概念本身,还会涉及与之相关的工作机制。
Java 流基本问题
分类
流分为两种:字节流和字符流。
区别
字节流在处理输入输出时不用到缓存,字符流会。
设计理念
Decorator设计模式(指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式)
Java序列化
Java有两种对象持久化的方式,序列化和外部序列化。
序列化含义
将对象转换为一连串字节流
为什么要序列化
进行远程通信时,无论何种数据,都是以二进制序列的形式在网络中传输的。对象等数据类型并不能直接被识别,因此需要转化。
序列化注意事项
- 能被序列化的类的子类也能被序列化
- 有两种声明类型的对象无法被序列化:static和transient
什么时候应该使用序列化?
- 网络传输数据
- 对象持久化到数据库或文件
- 为了实现深拷贝
Java实现序列化的具体步骤?
- implements Serializable接口
- 使用一个输出流来构造一个对象流对象
- 该对象流的writeObject方法可用于保存目标对象状态(写出目标对象)
- 要恢复目标对象可使用第二步对应的输入流
Java外部序列化
自行实现一个extends Serializable的外部序列化接口,且其中的读写方法也要自己写。
如何只序列化部分属性呢?
对于不想序列化的属性,可以用transient修饰。
并发与并行的区别是什么?
- 并发:concurrency,宏观上多个进程在同时执行的现象,此时微观上指令实际上是快速交替进行的
- 并行,parallel,由于多核机制,在微观上指令也是并行执行的
了解同步、异步、阻塞、非阻塞的概念和区别及联系吗?
| 多线程 | IO | |
|---|---|---|
| 同步 | 一个执行块同一时间只有一个线程可访问 | 发起IO操作并调用结束后,获取返回值后才执行下一步操作 |
| 异步 | 一个执行块同一时间多个线程可访问 | 发起IO操作后,结果由发起者轮询或执行者发起回调 |
| 阻塞 | 线程挂起 | 发起者发起IO操作后期间不能再处理其他业务 |
| 非阻塞 | 线程无挂起 | 发起者无需等待IO操作完成 |
几大IO模型
BIO(block,最经典)(方式:同步阻塞)
一个连接一个线程
缺点:允许创建的最大线程数可能远小于要处理的连接数
NIO(nonblock)(方式:异步阻塞)
多个连接一个线程,一个请求一个线程
通过Selector、Channels、Buffers实现。
后两个在学习操作系统和Go时都已经比较熟悉了,接下来讲讲Selector。
Selector直译为选择器,在这里的作用是轮询检查多个channel的状态,广义地讲就是判断通道是否可读或可写。
以下是关于Selector的示例代码:
服务端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
public static void main(String[] args) {
Selector selector=null;
try{
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(9000));
//NIO
ssc.configureBlocking(false);
selector=Selector.open();
//register channel
ssc.register(selector, SelectionKey.OP_ACCEPT);
//read buffer && write buffer
ByteBuffer rb=ByteBuffer.allocate(1024);
ByteBuffer wb=ByteBuffer.allocate(1024);
rb.put("hello client".getBytes());
wb.flip();
while(true){
int ready=selector.select();
if(ready==0) continue;
Set<SelectionKey> keys=selector.selectedKeys();
Iterator<SelectionKey> it=keys.iterator();
//iterator ready channel
while(it.hasNext()){
SelectionKey key=it.next();
if(key.isAcceptable()){
SocketChannel sc= ssc.accept();
sc.configureBlocking(false);
//register new connection to channel,read only
sc.register(selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
SocketChannel sc=(SocketChannel)key.channel();
rb.clear();
sc.read(rb);
rb.flip();
System.out.println("Server receive:"+new String(rb.array()));
//mark as write,to send message to client
key.interestOps(SelectionKey.OP_WRITE);
}else if(key.isWritable()){
wb.rewind();
SocketChannel sc=(SocketChannel)key.channel();
sc.write(wb);
//mark as read,to receive message from client
key.interestOps(SelectionKey.OP_READ);
}
it.remove();
}
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(selector!=null){
try{
selector.close();
}catch (IOException e){
}
}
}
}
}
client
package pra;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class client {
public static void main(String[] args) {
SocketChannel sc=null;
try{
sc=SocketChannel.open();
sc.connect(new InetSocketAddress(9000));
ByteBuffer wb=ByteBuffer.allocate(1024);
ByteBuffer rb=ByteBuffer.allocate(1024);
wb.put("Hello server".getBytes());
wb.flip();
while (true){
wb.rewind();
sc.write(wb);
rb.clear();
sc.read(rb);
System.out.println("client receive:"+new String(rb.array()));
}
}catch (IOException e){
e.printStackTrace();
}
finally {
if(sc!=null){
try{
sc.close();
}catch (IOException e){
}
}
}
}
}
AIO(Asynchronous I/O)
AIO基于Proactor模型实现。
首先讲讲什么是Proactor。
在进行IO读写相关的操作时,为了将请求与操作分离,会用到“事件分离器”,Reactor(响应器)和Proactor(直译为前摄器,可理解为主动)
- Reactor是非阻塞同步模式,感知就绪可读写事件;
- Proactor是异步模式,感知已完成的读写事件。
类比取快递。