精华内容
下载资源
问答
  • 主要介绍了Java文件读写IO/NIO及性能比较详细代码及总结,具有一定借鉴价值,需要的朋友可以参考下。
  • IO/NIO/AIO 基本概念

    千次阅读 2018-09-04 10:23:24
    1.阻塞IO—Socket Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或应答网络请求。 Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在...

    1.阻塞IO—Socket

    Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或应答网络请求。

    Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,不会因为在服务器端或在客户端而产生不同的级别。不管是ServerSocket还是Socket,它们的工作都是通过SocketImpl类及其子类完成的。

    套接字的连接过程可以分为四个步骤:服务器监听、客户端请求服务器、服务器端连接确认、客户端连接确认并进行通信。

    (1)服务器监听:服务器端套接字并不定位具体的客户端套接字,而是出于等待连接的状态,实时监控网络状态。

    (2)客户端请求:客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述要连接的服务器端的套接字,指出服务器端的套接字的地址和端口号,然后向服务器端套接字提出连接请求。

    (3)服务器端连接确认:当服务器端的套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发送给客户端。

    (4)客户端连接确认:一旦客户端确认了此描述,连接就建立好了,双方开始通信。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

    借用一下网上的Socket通信模型图片:

    Socket通信步骤:

    ①创建ServerSocket和Socket

    ②打开连接到Socket的输入/输出流

    ③按照协议对Socket进行读写操作

    ④关闭输入输出流、关闭Socket

    服务器端:

    ①创建ServerSocket对象,绑定监听端口

    ②通过accept()方法监听客户端请求

    ③建立连接后,通过输入流读取客户端发送的请求信息

    ④通过输出流向客户端发送响应信息

    ⑤关闭相关资源

    客户端:

    ①创建Socket对象,指明需要连接的服务器的地址和端口号

    ②连接建立后,通过输出流向服务器端发送请求信息

    ③通过输入流获取服务器响应信息

    ④关闭响应资源

    下面看一个简单的小例子:

    服务器端响应工具类:

     

     
    1. public class ServerHandler implements Runnable {

    2.  
    3. private Socket socket;

    4.  
    5. public ServerHandler(Socket socket) {

    6. this.socket = socket;

    7. }

    8.  
    9. @Override

    10. public void run() {

    11. BufferedReader bufferedReader = null;

    12. PrintWriter printWriter = null;

    13. try {

    14. bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    15. printWriter = new PrintWriter(socket.getOutputStream(), true);

    16.  
    17. while (true) {

    18. String info = bufferedReader.readLine();

    19. if(info == null)

    20. break;

    21. System.out.println("客户端发送的消息:" + info);

    22. printWriter.println("服务器端响应了客户端请求....");

    23. }

    24. } catch (Exception e) {

    25. e.printStackTrace();

    26. } finally {

    27. if(bufferedReader != null){

    28. try {

    29. bufferedReader.close();

    30. } catch (IOException e) {

    31. e.printStackTrace();

    32. }

    33. }

    34. if(printWriter != null){

    35. try {

    36. printWriter.close();

    37. } catch (Exception e) {

    38. e.printStackTrace();

    39. }

    40. }

    41. if(socket != null){

    42. try {

    43. socket.close();

    44. } catch (IOException e) {

    45. e.printStackTrace();

    46. }

    47. }

    48. socket = null;

    49. }

    50. }

    51. }

    服务器端:

     

     

     
    1. public class Server {

    2. private static int PORT = 8379;

    3. public static void main(String[] args) {

    4. ServerSocket serverSocket = null;

    5. try {

    6. serverSocket = new ServerSocket(PORT);

    7. System.out.println("服务器端启动了....");

    8. //进行阻塞

    9. Socket socket = serverSocket.accept();

    10. //启动一个线程来处理客户端请求

    11. new Thread(new ServerHandler(socket)).start();

    12. } catch (Exception e) {

    13. e.printStackTrace();

    14. } finally {

    15. if(serverSocket != null){

    16. try {

    17. serverSocket.close();

    18. } catch (IOException e) {

    19. e.printStackTrace();

    20. }

    21. }

    22. serverSocket = null;

    23. }

    24. }

    25. }

    客户端:

     

     

     
    1. public class Client {

    2. private static int PORT = 8379;

    3. private static String IP = "127.0.0.1";

    4.  
    5. public static void main(String[] args) {

    6. BufferedReader bufferedReader = null;

    7. PrintWriter printWriter = null;

    8. Socket socket = null;

    9. try {

    10. socket = new Socket(IP, PORT);

    11. printWriter = new PrintWriter(socket.getOutputStream(), true);

    12. bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    13.  
    14. printWriter.println("客户端请求了服务器....");

    15. String response = bufferedReader.readLine();

    16. System.out.println("Client:" + response);

    17. } catch (Exception e) {

    18. e.printStackTrace();

    19. } finally {

    20. if(bufferedReader != null){

    21. try {

    22. bufferedReader.close();

    23. } catch (IOException e) {

    24. e.printStackTrace();

    25. }

    26. }

    27. if(printWriter != null){

    28. try {

    29. printWriter.close();

    30. } catch (Exception e) {

    31. e.printStackTrace();

    32. }

    33. }

    34. if(socket != null){

    35. try {

    36. socket.close();

    37. } catch (IOException e) {

    38. e.printStackTrace();

    39. }

    40. } else {

    41. socket = null;

    42. }

    43. }

    44. }

    45. }

    运行结果:

     


    以上的代码有个问题,就是每次有客户端请求服务器端都会创建一个线程,当线程过多时,服务器端可能会宕机。解决这个问题,可以使用JDK提供的线程池(伪异步)。其它地方都不变,将服务器端的代码修改成如下即可:

     

     
    1. public class Server {

    2. private static int PORT = 8379;

    3. public static void main(String[] args) {

    4. ServerSocket serverSocket = null;

    5. try {

    6. serverSocket = new ServerSocket(PORT);

    7. System.out.println("服务器端启动了....");

    8. //进行阻塞

    9. Socket socket = null;

    10. //启动一个线程来处理客户端请求

    11. //new Thread(new ServerHandler(socket)).start();

    12. HandlerExecutorPool pool = new HandlerExecutorPool(50, 1000);

    13. while (true) {

    14. socket = serverSocket.accept();

    15. pool.execute(new ServerHandler(socket));

    16. }

    17. } catch (Exception e) {

    18. e.printStackTrace();

    19. } finally {

    20. if(serverSocket != null){

    21. try {

    22. serverSocket.close();

    23. } catch (IOException e) {

    24. e.printStackTrace();

    25. }

    26. }

    27. serverSocket = null;

    28. }

    29. }

    30. }

    其中HandlerExecutorPool为自定义的线程池,代码如下:

     
    1. public class HandlerExecutorPool {

    2.  
    3. private ExecutorService executor;

    4.  
    5. public HandlerExecutorPool(int maxSize, int queueSize) {

    6. this.executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxSize, 120L, TimeUnit.SECONDS,

     
    1. new ArrayBlockingQueue<>(queueSize));

    2. }

    3.  
    4. public void execute(Runnable task) {

    5. executor.execute(task);

    6. }

    7. }

    2.IO(BIO)与NIO的区别

    其本质就是阻塞和非阻塞的区别。

    阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,那么程序就一直等着,直到传输完毕为止。

    非阻塞概念:应用程序直接可以获取已经准备就绪的数据,无需等待。

    IO为同步阻塞形式,NIO为同步非阻塞形式。NIO没有实现异步,在JDK1.7之后,升级了NIO库包,支持异步非阻塞通信模型,即NIO2.0(AIO)。

    同步和异步:同步和异步一般是面向操作系统与应用程序对IO操作的层面上来区别的。①同步时,应用程序会直接参与IO读写操作,并且应用程序会直接阻塞到某一个方法上,直到数据准备就绪(BIO);或者采用轮询的策略实时检查数据的就绪状态,如果就绪则获取数据(NIO)。②异步时,则所有的IO读写操作都交给操作系统处理,与应用程序没有直接关系,应用程序并不关心IO读写,当操作系统完成IO读写操作时,会向应用程序发出通知,应用程序直接获取数据即可。

    同步说的是Server服务端的执行方式,阻塞说的是具体的技术,接收数据的方式、状态(io、nio)。

    3.NIO编程介绍

    学习NIO编程,首先需要了解几个概念:

    (1)Buffer(缓冲区)

    Buffer是一个对象,它包含一些需要写入或者读取的数据。在NIO类库中加入Buffer对象,体现了新类库与原IO的一个重要区别。在面向流的IO中,可以直接将数据写入或读取到Stream对象中。在NIO类库中,所有的数据都是用缓冲区处理的(读写)。 缓冲区实质上是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类型的数组。这个数组为缓冲区提供了访问数据的读写等操作属性,如位置、容量、上限等概念,具体的可以参考API文档。

    Buffer类型:最常使用的是ByteBuffer,实际上每一种java基本类型都对应了一种缓存区(除了Boolean类型)。

    ①ByteBuffer②CharBuffer③ShortBuffer④IntBuffer⑤LongBuffer⑥FloatBuffer⑦DoubleBuffer

    (2)Channel(管道、通道)

    Channel就像自来水管道一样,网络数据通过Channel读取和写入,通道与流的不同之处在于通道是双向的,而流只能在一个方向上移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读、写或者二者同时进行,最关键的是可以和多路复用器集合起来,有多种的状态位,方便多路复用器去识别。通道分为两大类:一类是用于网络读写的SelectableChannel,另一类是用于文件操作的FileChannel,我们使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子类。

     

    (3)Selector(选择器、多路复用器)

    是NIO编程的基础,非常重要。多路复用器提供选择已经就绪的任务的能力。简单说,就是Selector会不断的轮询注册在其上的通道(Channel),如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel集合,从而进行后续的IO操作。一个多路复用器(Selector)可以负责成千上万的通道(Channel),没有上限。这也是JDK使用了epoll代替传统的select实现,获得连接句柄(客户端)没有限制。那也就意味着我们只要一个线程负责Selector的轮询,就可以接入成千上万个客户端,这是JDK NIO库的巨大进步。

    Selector线程类似一个管理者(Master),管理了成千上万个管道,然后轮询哪个管道的数据已经准备好了,通知CPU执行IO的读取或写入操作。

    Selector模式:当IO事件(管道)注册到选择器以后,Selector会分配给每个管道一个key值,相当于标签。Selector选择器是以轮询的方式进行查找注册的所有IO事件(管道),当IO事件(管道)准备就绪后,Selector就会识别,会通过key值来找到相应的管道,进行相关的数据处理操作(从管道中读取或写入数据,写到缓冲区中)。每个管道都会对选择器进行注册不同的事件状态,以便选择器查找。

    事件状态:

    SelectionKey.OP_CONNECT

    SelectionKey.OP_ACCEPT

    SelectionKey.OP_READ

    SelectionKey.OP_WRITE

    NIO通信模型图解:

    (虚线表示不直接相关联)

    下面用代码来演示一下Buffer、Channel、Selector的使用。

    以IntBuffer为例,讲解一下Buffer的常用API:

     

     
    1. public class IntBufferTest {

    2. public static void main(String[] args) {

    3. //1、基本操作

    4. //创建指定长度的缓冲区

    5. /*IntBuffer buffer = IntBuffer.allocate(10);

    6. buffer.put(11); //position位置:0->1

    7. buffer.put(5); //position位置:1->2

    8. buffer.put(32); //position位置:2->3

    9. System.out.println("未调用flip复位方法前的buffer:" + buffer);

    10. //把位置复位为0,也就是position位置由3->0

    11. buffer.flip();

    12. //比较未调用flip方法和调用之后buffer的limit可以发现,不进行复位操作的话,position的值为3,limit的值为10

    13. // 因为缓冲区中已有11、5、32三个元素,也就意味着put()方法会使position向后递增1

    14. System.out.println("调用flip复位方法后的buffer:" + buffer);

    15. System.out.println("buffer容量为:" + buffer.capacity());

    16. System.out.println("buffer限制为:" + buffer.limit());

    17. System.out.println("获取下标为1的元素:" + buffer.get(1));

    18. System.out.println("调用get(index)方法后的buffer:" + buffer); //调用get(index)方法,不会改变position的值

    19. buffer.put(1, 4); //将buffer位置为1的值替换为4,调用put(index,value)不会改变position的值

    20. System.out.println("调用put(index, value)方法后的buffer:" + buffer);

    21.  
    22. for(int i=0; i<buffer.limit(); i++) {

    23. //调用get方法会使缓冲区的位置(position)向后递增一位

    24. System.out.print(buffer.get() + "\t");

    25. }

    26. System.out.println("\nbuffer对象遍历之后buffer为:" + buffer);*/

    27.  
    28.  
    29. //2、wrap方法的使用

    30. /*int[] arr = new int[]{1, 2, 3};

    31. IntBuffer buffer = IntBuffer.wrap(arr);

    32. System.out.println("wrap(arr)方法:" + buffer);

    33. //IntBuffer.wrap(array, postion, length)表示容量为array的长度,但是可操作的元素为位置postion到length的数组元素

    34. buffer = IntBuffer.wrap(arr, 0, 2);

    35. System.out.println("wrap(arr, 0, 2):" + buffer);*/

    36.  
    37. //3、其他方法

    38. IntBuffer buffer = IntBuffer.allocate(10);

    39. int[] arr = new int[]{1, 2, 3};

    40. buffer.put(arr);

    41. System.out.println("调用put(arr)方法后的buffer:" + buffer);

    42. //一种复制方法,buffer1的pos、lim、cap与buffer的一样

    43. IntBuffer buffer1 = buffer.duplicate();

    44. System.out.println("buffer1:" + buffer1);

    45.  
    46. buffer.position(1); //将buffer的position设置为1,不建议使用。功能相当于flip()方法,但是从运行结果可以看出,lim依然等于10

    47. System.out.println("调用position()方法后的buffer:" + buffer);

    48. System.out.println("buffer的可读数据量:" + buffer.remaining()); //计算出从pos到lim的长度

    49. int[] arr1 = new int[buffer.remaining()];

    50. //将缓冲区的数据放入arr1中

    51. buffer.get(arr1);

    52. for(Integer i : arr1) {

    53. System.out.print(Integer.toString(i) + ",");

    54. }

    55. System.out.println();

    56.  
    57. //比较flip()方法和position(index)方法的区别

    58. buffer1.flip();

    59. System.out.println("buffer1的可读数量:" + buffer1.remaining());

    60. arr1 = new int[buffer1.remaining()];

    61. buffer1.get(arr1);

    62. for(Integer i : arr1) {

    63. System.out.print(Integer.toString(i) + ",");

    64. }

    65. }

    66. }

    运行结果:

     

    接下来是Buffer、Channel、Selector的一个入门的小例子:

    Server端:

     

     
    1. public class Server implements Runnable {

    2.  
    3. private Selector selector;

    4. private ByteBuffer buffer = ByteBuffer.allocate(1024);

    5.  
    6. public Server(int port) {

    7. try {

    8. //1 打开多复用器

    9. selector = Selector.open();

    10. //2 打开服务器通道

    11. ServerSocketChannel ssc = ServerSocketChannel.open();

    12. //3 设置服务器通道为非阻塞方式

    13. ssc.configureBlocking(false);

    14. //4 绑定地址

    15. ssc.bind(new InetSocketAddress(port));

    16. //5 把服务器通道注册到多路复用选择器上,并监听阻塞状态

    17. ssc.register(selector, SelectionKey.OP_ACCEPT);

    18. System.out.println("Server start, port:" + port);

    19. } catch (IOException e) {

    20. e.printStackTrace();

    21. }

    22. }

    23.  
    24. @Override

    25. public void run() {

    26. while (true) {

    27. try {

    28. //1 必须让多路复用选择器开始监听

    29. selector.select();

    30. //2 返回所有已经注册到多路复用选择器上的通道的SelectionKey

    31. Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

    32. //3 遍历keys

    33. while (keys.hasNext()) {

    34. SelectionKey key = keys.next();

    35. keys.remove();

    36. if(key.isValid()) { //如果key的状态是有效的

    37. if(key.isAcceptable()) { //如果key是阻塞状态,则调用accept()方法

    38. accept(key);

    39. }

    40. if(key.isReadable()) { //如果key是可读状态,则调用read()方法

    41. read(key);

    42. }

    43. }

    44. }

    45. } catch (IOException e) {

    46. e.printStackTrace();

    47. }

    48. }

    49. }

    50.  
    51. private void accept(SelectionKey key) {

    52. try {

    53. //1 获取服务器通道

    54. ServerSocketChannel ssc = (ServerSocketChannel) key.channel();

    55. //2 执行阻塞方法

    56. SocketChannel sc = ssc.accept();

    57. //3 设置阻塞模式为非阻塞

    58. sc.configureBlocking(false);

    59. //4 注册到多路复用选择器上,并设置读取标识

    60. sc.register(selector, SelectionKey.OP_READ);

    61. } catch (Exception e) {

    62. e.printStackTrace();

    63. }

    64. }

    65.  
    66. private void read(SelectionKey key) {

    67. try {

    68. //1 清空缓冲区中的旧数据

    69. buffer.clear();

    70. //2 获取之前注册的SocketChannel通道

    71. SocketChannel sc = (SocketChannel) key.channel();

    72. //3 将sc中的数据放入buffer中

    73. int count = sc.read(buffer);

    74. if(count == -1) { // == -1表示通道中没有数据

    75. key.channel().close();

    76. key.cancel();

    77. return;

    78. }

    79. //读取到了数据,将buffer的position复位到0

    80. buffer.flip();

    81. byte[] bytes = new byte[buffer.remaining()];

    82. //将buffer中的数据写入byte[]中

    83. buffer.get(bytes);

    84. String body = new String(bytes).trim();

    85. System.out.println("Server:" + body);

    86. } catch (Exception e) {

    87. e.printStackTrace();

    88. }

    89. }

    90.  
    91. public static void main(String[] args) {

    92. new Thread(new Server(8379)).start();

    93. }

    94. }

    客户端:

     

     

     
    1. public class Client {

    2. public static void main(String[] args) {

    3. InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8379);

    4. SocketChannel sc = null;

    5. ByteBuffer buffer = ByteBuffer.allocate(1024);

    6. try {

    7. //打开通道

    8. sc = SocketChannel.open();

    9. //建立连接

    10. sc.connect(address);

    11. while (true) {

    12. byte[] bytes = new byte[1024];

    13. System.in.read(bytes);

    14. //把输入的数据放入buffer缓冲区

    15. buffer.put(bytes);

    16. //复位操作

    17. buffer.flip();

    18. //将buffer的数据写入通道

    19. sc.write(buffer);

    20. //清空缓冲区中的数据

    21. buffer.clear();

    22. }

    23. } catch (Exception e) {

    24. e.printStackTrace();

    25. } finally {

    26. if(sc != null) {

    27. try {

    28. sc.close();

    29. } catch (IOException e) {

    30. e.printStackTrace();

    31. }

    32. }

    33. }

    34. }

    35. }

    运行结果:

     



    3、AIO

    在NIO的基础上引入了异步通道的概念,并提供了异步文件和异步套接字通道的实现,从而在真正意义上实现了异步非阻塞,之前的NIO只是非阻塞而并非异步。AIO不需要通过对多路复用器对注册的通道进行轮询操作即可实现异步读写,从而简化NIO编程模型。

    ①AsynchronousServerSocketChannel

    ②AsynchronousSocketChannel

    下面看代码:

    Server端:

     

     
    1. public class Server {

    2. //线程池

    3. private ExecutorService executorService;

    4. //线程组

    5. private AsynchronousChannelGroup channelGroup;

    6. //服务器通道

    7. public AsynchronousServerSocketChannel channel;

    8.  
    9. public Server(int port) {

    10. try {

    11. //创建线程池

    12. executorService = Executors.newCachedThreadPool();

    13. //创建线程组

    14. channelGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);

    15. //创建服务器通道

    16. channel = AsynchronousServerSocketChannel.open(channelGroup);

    17. //绑定地址

    18. channel.bind(new InetSocketAddress(port));

    19. System.out.println("server start, port:" + port);

    20. channel.accept(this, new ServerCompletionHandler());

    21. Thread.sleep(Integer.MAX_VALUE);

    22. } catch (Exception e) {

    23. e.printStackTrace();

    24. }

    25. }

    26.  
    27. public static void main(String[] args) {

    28. Server server = new Server(8379);

    29. }

    30. }

    ServerCompletionHandler类:

     
    1. public class ServerCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, Server> {

    2. @Override

    3. public void completed(AsynchronousSocketChannel channel, Server attachment) {

    4. //当有下一个客户端接入的时候,直接调用Server的accept方法,这样反复执行下去,保证多个客户端都可以阻塞

    5. attachment.channel.accept(attachment, this);

    6. read(channel);

    7. }

    8.  
    9. private void read(AsynchronousSocketChannel channel) {

    10. //读取数据

    11. ByteBuffer buffer = ByteBuffer.allocate(1024);

    12. channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {

    13. @Override

    14. public void completed(Integer resultSize, ByteBuffer attachment) {

    15. attachment.flip();

    16. System.out.println("Server->" + "收到客户端发送的数据长度为:" + resultSize);

    17. String data = new String(buffer.array()).trim();

    18. System.out.println("Server->" + "收到客户端发送的数据为:" + data);

    19. String response = "服务器端响应了客户端。。。。。。";

    20. write(channel, response);

    21. }

    22.  
    23. @Override

    24. public void failed(Throwable exc, ByteBuffer attachment) {

    25. exc.printStackTrace();

    26. }

    27. });

    28. }

    29.  
    30. private void write(AsynchronousSocketChannel channel, String response) {

    31. try {

    32. ByteBuffer buffer = ByteBuffer.allocate(1024);

    33. buffer.put(response.getBytes());

    34. buffer.flip();

    35. channel.write(buffer).get();

    36. } catch (Exception e) {

    37. e.printStackTrace();

    38. }

    39. }

    40.  
    41. @Override

    42. public void failed(Throwable exc, Server attachment) {

    43. exc.printStackTrace();

    44. }

    45. }

    客户端:

     
    1. public class Client implements Runnable {

    2.  
    3. private AsynchronousSocketChannel channel;

    4.  
    5. public Client() throws IOException {

    6. channel = AsynchronousSocketChannel.open();

    7. }

    8.  
    9. public void connect() {

    10. channel.connect(new InetSocketAddress("127.0.0.1", 8379));

    11. }

    12.  
    13. public void write(String data) {

    14. try {

    15. channel.write(ByteBuffer.wrap(data.getBytes())).get();

    16. read();

    17. } catch (Exception e) {

    18. e.printStackTrace();

    19. }

    20. }

    21.  
    22. public void read() {

    23. ByteBuffer buffer = ByteBuffer.allocate(1024);

    24. try {

    25. channel.read(buffer).get();

    26. buffer.flip();

    27. byte[] bytes = new byte[buffer.remaining()];

    28. buffer.get(bytes);

    29. String data = new String(bytes, "UTF-8").trim();

    30. System.out.println(data);

    31. } catch (Exception e) {

    32. e.printStackTrace();

    33. }

    34. }

    35.  
    36. @Override

    37. public void run() {

    38. while (true) {

    39.  
    40. }

    41. }

    42.  
    43. public static void main(String[] args) {

    44. try {

    45. Client c1 = new Client();

    46. Client c2 = new Client();

    47. Client c3 = new Client();

    48.  
    49. c1.connect();

    50. c2.connect();

    51. c3.connect();

    52.  
    53. new Thread(c1).start();

    54. new Thread(c2).start();

    55. new Thread(c3).start();

    56.  
    57. Thread.sleep(1000);

    58.  
    59. c1.write("c1 aaa");

    60. c2.write("c2 bbbb");

    61. c3.write("c3 ccccc");

    62. } catch (Exception e) {

    63. e.printStackTrace();

    64. }

    65. }

    66. }

    运行结果:

     

    展开全文
  • java学习笔记1(java io/nio)设计模式
  • 面经·Java·(B)IO/NIO/AIO

    千次阅读 2018-08-10 23:14:29
    Java中高手加QQ群498238384 备注:Java中高手- 呢称   如果对您有帮助,麻烦在右边点个赞哦~~   什么是IONIO、AIO?... NIO 指新IO,核心是非阻塞,解决传统IO的阻塞问题。操作对象是Buffe...

    Java中高手加QQ群498238384 备注:Java中高手- 呢称

     

    如果对您有帮助,麻烦在右边点个赞哦~~

     

    • 什么是IO、NIO、AIO?
    • 讲一讲Channel 和 Buffer
    • 讲一讲selector
    • directBuffer和buffer的区别

     

     

    • 什么是IO、NIO、AIO?
      • NIO 指新IO,核心是非阻塞,解决传统IO的阻塞问题。操作对象是Buffer

        • 其实NIO的核心是IO线程池,一定要记住这个关键点。

        • NIO中的IO多路复用调用系统级别的select和poll模型,由系统进行监控IO状态,避免用户线程通过反复尝试的方式查询状态

      • AIO即Asynchronous I/O(异步非阻塞 I/O),这是Java 1.7引入的NIO 2.0中用到的。

    • 讲一讲Channel 
      • 一个Channel(通道)代表和某一实体的连接,这个实体可以是文件、网络套接字等。既可以读又可以写。而流是单向的。

      •  

        •  
    • 讲一讲selector
      • Selector 为Channel服务,Channel为要访问的数据服务。

      • selector采集各个channel的状态(或者说监听事件:

        • Accept:有可以接受的连接
        • Connect:连接成功
        • Read:有数据可读
        • Write:可以写入数据了
        • 程序员通过将通道注册到选择器,并设置好关心的事件,然后就可以通过调用select()方法,静静地等待事件发生。 避免了反复主动尝试Read()、write()、accept()、connnect()操作。

          作用举例:避免因为没有数据到达而反复尝试read().(耗费CPU)。

      • 为什么要用selector,类似于为什么要select的问题

        • 前文说了,如果用阻塞I/O,需要多线程(浪费内存)如果用非阻塞I/O,需要不断重试(耗费CPU)。Selector的出现解决了这尴尬的问题,非阻塞模式下,通过Selector,我们的线程只为已就绪的通道工作(约等于已准备好的数据),不用盲目的重试了。比如,当所有通道都没有数据到达时,也就没有Read事件发生,我们的线程会在select()方法处被挂起,从而让出了CPU资源。

    •  

      Buffer

       

      • Buffer是缓冲区,不像传统IO中那样直接使用byte数组,它是封装过的。

         

      • (注意:将Buffer数据 写入 通道是通道 读取 操作,从通道 读取 数据到Buffer是Buffer 写入 操作)

      • 与Java基本类型相对应,NIO提供了多种 Buffer 类型,如ByteBuffer、CharBuffer、IntBuffer等,区别就是读写缓冲区时的单位长度不一样(以对应类型的变量为单位进行读写)。

        Buffer中有3个很重要的变量,它们是理解Buffer工作机制的关键,分别是

        • capacity (总容量)
        • position (指针当前位置)
        • limit (读/写边界位置)
    •  

       

      directBuffer和buffer的区别

     

    展开全文
  • 概要:阻塞IO模型非阻塞IO模型多路复用IO模型信号驱动IO模型异步IO模型Java IOJava NIO--------------------------------------------------------------------------------------------1、阻塞IO模型 最传统的一种...

    概要:

    阻塞IO模型

    非阻塞IO模型

    多路复用IO模型

    信号驱动IO模型

    异步IO模型

    Java IO

    Java NIO

    --------------------------------------------------------------------------------------------

    1、阻塞IO模型

        最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解绑block状态。典型的阻塞IO模型的例子为:data = socket.read();如果数据没有就绪,就会一直阻塞在read方法。

    2、非阻塞IO模型

        当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个Error时,它就知道数据还没准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次受到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就是说非阻塞IO模型不会交出CPU,而会一直占用CPU。典型的非阻塞IO模型一般如下:

    while(true){
      data = socket.read();
      if(data != error){
        处理数据
        break;
      }
    }

    但是对于非阻塞IO就有一个严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率过高,因此一般情况下很少使用while循环这种方式来读取数据

    3、多路复用IO模型

        多路复用IO模型是目前使用比较多的模型。Java NIO实际上就是多路复用IO。在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的线程或者进程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。在Java NIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。多路复用IO模式,通过一个线程就可以管理多个socket,只有在真正有socket读写事件进行时,才会使用IO资源进行实际的读写操作。因此,多路复用IO比较适合连接数比较多的情况。

        另外多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断地询问socket状态时通过用户线程去进行的,而在多路复用IO中,轮询每个socket状态是内核进行的,这个效率比用户线程要高的多。

        不过要注意的是,多路复用IO模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行相应。因此对于多路复用IO模型来说,一旦事件响应体很大,那么会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询

    4、信号驱动IO模型

        在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。

    5、异步IO模型

        异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它收到一个asynchronous read之后,它会立刻返回,说明read请求一个成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就是说用户线程完全不需要实际的整个IO操作是如何进行的。只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。

        也就是说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成的。然后发送一个信号告知用户线程操作已经完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用IO函数进行实际的读写操作了。

    注意:异步IO是需要操作系统的底层支持的,在java7中,提供了Asynchronous IO。

    6、Java IO 包

        

    7、Java NIO

        NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。


    NIO和传统IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的

        7.1、NIO的缓冲区

            Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,他们没有被缓存在任何地方。此外,它不能前后移动数据中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。NIO的缓冲导向方法不同。数据读取到一个它稍后处理的缓冲区,需要时可以在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有你需要处理的数据。而且,需要确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未被处理的数据。

        7.2、NIO的非阻塞

            IO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。NIO的非阻塞模式,使一个线程从某通道发送请求读数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其它的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(Channel)。

        7.3、Java提供的NIO

    Channel

    首先说一下Channel,国内大多翻译成“通道”。Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。 
    NIO中的Channel的主要实现有:

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel

    这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client)。下面演示的案例基本上就是围绕这4个类型的Channel进行陈述的。

    Buffer

    Buffer顾名思义:缓冲区,实际上是一个容器,一个连续数组。Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。如下图:


    上图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入buffer中,然后将buffer中的内容写入通道,服务端这边接收数据必须通过channel将数据读入到buffer中,然后再从buffer中取出数据来处理。

    NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。

    Selector

        Selector类是NIO的核心类。Selector能够检测多个注册的通道上是否有事件发送,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道。也就是管理多个连接。这样使得只有在连接真正有读写事件发送时,才会调用函数来进行读写,就大大减少了系统开销,并且不必为每个连接创建一个线程,不用去维护多个线程,并且避免了多线程之间上下文切换导致的开销。

    -----------------------------------------------------------------------------------------------------

    扩展阅读(参考文献):

    Java NIO

    展开全文
  • java.lang.NoClassDefFoundError: io/netty/channel/nio/NioEventLoopGroup 在搭建dubbo的时候暴了这个错误。 但是 dubbo 其实是有依赖  org.jboss 的netty 而且 本地maven 仓库确实是有成功下载 netty...
    2019-07-07 08:25:39.734] boot -  WARN [main] --- QosProtocolWrapper:  [DUBBO] Fail to start qos server: , dubbo version: 2.6.4, current host: 192.168.0.103
    java.lang.NoClassDefFoundError: io/netty/channel/nio/NioEventLoopGroup

    在搭建dubbo的时候暴了这个错误。

    但是 dubbo 其实是有依赖 

    org.jboss  的netty

    而且 本地maven 仓库确实是有成功下载 netty的。

    是缺少io.netty ? 引入 

    <groupId>io.netty</groupId>
    <artifactId>netty</artifactId>

    发现还是不可以。而且 其他 jar其实是有 依赖 io.netty 的。也就是说 项目是有导入 io.netty的

    一些网友 爆的相关这些错误,有些是导入 全的 netty 包,

    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>

    导入 全的 netty-all  解决

     

     

    转载于:https://my.oschina.net/ouminzy/blog/3070591

    展开全文
  • 最近在做dubbo项目,今天升级了下dubbo的版本,然后就掉坑里去了,选择...java.lang.NoClassDefFoundError: io/netty/channel/nio/NioEventLoopGroup at com.alibaba.dubbo.qos.server.Server.start(Server.java:84) ...
  • io/nio各种文件读写方法。文件压缩成jar包。
  • 常见JAVA IO/NIO模型

    万次阅读 2019-07-09 12:33:28
    我们常见的IO模型有:阻塞 IO 模型、非阻塞 IO 模型、多路复用 IO 模型、信号驱动 IO 模型、异步 IO 模型;下面我们就简单介绍一下以上IO模型。 1、阻塞 IO 模型 最传统的一种IO 模型,即在读写数据过程中会发生...
  • Java IO/NIO教程

    2019-03-02 01:07:55
    Java IO教程 http://tutorials.jenkov.com/java-io/index.html ...英文版:http://tutorials.jenkov.com/java-nio/index.html 中文版: Java NIO概述 Java NIO Channel Java NIO Buffer Java NIO Scatter / Gat...
  • JAVA 面试随笔( IO / NIO

    万次阅读 2020-08-04 09:49:18
    2.8.1. 阻塞 IO 模型 最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO 请求之后,内 核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用 户线程交出...
  • io/netty/channel/nio/NioEventLoopGrou

    千次阅读 2018-12-18 16:17:20
    最近项目不忙,多学习,按着官方给的配置文件来建项目可是运行时报错java.lang.NoClassDefFoundError: io/netty/channel/nio/NioEventLoopGroup我也是醉了。懵逼了半天。 找了半天资料牛头不对马嘴,后来自己试了...
  • IO/NIO】Java NIO浅析

    千次阅读 2017-08-06 21:47:48
    NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。那么NIO的...
  • IONIO的区别

    2020-10-12 20:56:22
    1.IONIO的区别 NIO就是New IO在JDK1.4中引入。 IONIO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO快不少。 在Java API中提供了两套NIO,一套针对标准输入输出NIO,另一套就是...
  • Java IO、 Java NIO、Java网络编程 2、面试题 io流中的几个面试题 java IO流面试题和选择题 002java面试笔记——【java基础篇】从团800失败面试总结的java面试题 【面试之java io流】IO Java IO和网络...
  • IO/NIO】Java IO/NIO

    千次阅读 2017-07-30 21:50:27
    java.io 包定义了多个流类型:  1) 按数据流方向分为 – 输入流和输出流  2) 按数据单位分为 – 字节流和字符流  3) 按功能分为 – 节点流和处理流所有流类型均位于java.io包内,分别继承以下四种抽象流...
  • IO NIO NIO2(AIO) 概述

    千次阅读 2019-07-30 15:43:45
    在我们学习Java的IO流之前,我们都要了解几个关键词 同步与异步(synchronous/asynchronous):同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;而异步则相反...
  • 2020 最新IONIO面试题

    千次阅读 2020-05-14 14:21:33
    1、Java中IO 流? Java 中 IO 流分为几种? 按照流的流向分,可以分为输入流和输出流; 按照操作单元划分,可以划分为字节流和字符流; 按照流的角色划分为节点流和处理流。 Java Io 流共涉及 40 多个类,这些类看...
  • 5分钟搞懂ionio、bio

    千次阅读 2021-01-27 17:29:16
    面试官经常问ionio的区别,如果把ionio放一起比较的话,那这里的io其实可以理解为bio,即blocking-io: bio:同步阻塞 bio是java传统的io模型,他是同步阻塞io,一个线程触发io操作后,必须等待这个io操作执行...
  • java IO/NIO 下载上传的笔记

    千次阅读 2015-10-19 21:28:55
    使用web的方式上传下载文件的时候,可以根据是否独占线程,是否阻塞等限制来选择使用NIO/IO. 当使用单独的文本服务器提供文本服务的时候,一般选用NIO的方式,如果只是简单的文件上传下载,选择自己觉得最简单的就...
  • 本来想把标题写的更精细点,便于遇到同样...首先,这是我在Android中使用org.apche.commons.io库做文件夹的递归删除时遇到的一个小坑。 其次,我的解决方法只针对我自己的场景去说,可能无法覆盖全面,但是你知道问题
  • Java中的IONIO面试题

    千次阅读 2020-05-07 19:57:55
    2、 Java IONIO的区别3、常用io类有那些4、字节流与字符流的区别5、阻塞 IO 模型6、非阻塞 IO 模型7、多路复用 IO 模型8、信号驱动 IO 模型9、异步 IO 模型10、JAVA NIO11、NIO 的缓冲区12、NIO 的非阻塞13、...
  • java IO/NIO操作文件

    2011-06-27 14:31:00
    java中IO流的操作可以说是javaSE中我们必须掌握的部分,javaNIO可能对于我们在实际中使用的不多,但是作为加快文件操作的一些特性我们还是有了解的必要。因此下要给出几个比较简单的例子,跟大家一起讨论。 package...
  • 报错如下: 解决办法: 在pom中加入依赖: <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</...
  • java中IONIO的区别和适用场景

    千次阅读 2015-05-20 18:05:49
    这几天主要学习了NIO,因为之前对IO使用的也比较多,所以有一个简单的对比,并且把学习的成果记录下来。   java.NIO包里包括三个基本的组件 l buffer:因为NIO是基于缓冲的,所以buffer是最底层的必要类,这...
  • JAVA IONIO区别及应用场景

    千次阅读 2018-01-23 17:13:03
    IO:阻塞的,面向流的。 Java IO是阻塞式的操作,当一个inputstream或outputstream在进行read()或write()操作时,是一直处于等待状态的,直到有数据读/写入后才进行处理 Java IO面向流意味着每次从流中读一...
  • Java IO NIO and NIO 2 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn...
  • NIO,同步非阻塞IO,简单理解:一个线程处理多个连接,发起IO请求是非阻塞的但处理IO请求是同步的 NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络...
  • nested exception is java.lang.NoSuchMethodError: org.apache.commons.io.FileUtils.copyInputStreamToFile(Ljava/io/InputStream;Ljava/io/File;)V at org.springframework.web.servlet.DispatcherServlet....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 126,054
精华内容 50,421
关键字:

IO/NIO