Java非同步I/O


在同步檔案I/O中,對I/O操作的請求將等待,直到I/O操作完成。在非同步檔案I/O中,I/O操作的請求由系統非同步執行。當系統完成檔案I/O時,它通知應用程式其請求的完成。

java.nio.channels.AsynchronousFileChannel類表示非同步檔案通道。使用AsynchronousFileChannel類的靜態open()方法獲取AsynchronousFileChannel類的範例。

以下程式碼顯示了如何獲取WRITE的非同步檔案通道。

Path  path   = Paths.get("C:\\Java_Dev\\rainbow.txt");
AsynchronousFileChannel afc   = AsynchronousFileChannel.open(path, WRITE,  CREATE);

AsynchronousFileChannel提供了兩種方法來處理非同步檔案I/O操作的結果。

  • 使用java.util.concurrent.Future物件。
  • 使用java.nio.channels.CompletionHandler 物件。

支援非同步檔案I/O操作的AsynchronousFileChannel類的每個方法有兩個版本。一個版本返回一個Future物件,可以使用它來處理所請求的非同步操作的結果。

Future物件的get()方法返回寫入檔案通道的位元組數。以下程式碼使用返回Future物件的write()方法的版本:

ByteBuffer dataBuffer  = a buffer;
long  startPosition = 0;
Future<Integer> result = afc.write(dataBuffer, startPosition);

當得到一個Future物件後,可以使用輪詢方法或阻塞等待方法來處理非同步檔案I/O的結果。

下面的程式碼顯示了輪詢方法,它將繼續呼叫Future物件的isDone()方法來檢查I/O操作是否完成:

while (!result.isDone()) {
}
int writtenNumberOfBytes = result.get();

AsynchronousFileChannel類的另一個版本的方法獲得一個CompletionHandler物件,當請求的非同步I/O操作完成或失敗時,該物件的方法被呼叫。

CompletionHandler介面有兩個方法:completed()failed()。當所請求的I/O操作成功完成時,將呼叫completed()方法。當請求的I/O操作失敗時,將呼叫failed()方法。

以下程式碼使用Attachment類的物件作為完成處理程式的附件:

class  Attachment {
    public Path  path;
    public  ByteBuffer buffer;
    public  AsynchronousFileChannel asyncChannel;
}
class MyHandler implements CompletionHandler<Integer,  Attachment>   {
    @Override
    public void  completed(Integer result, Attachment attach)  {
        // Handle  completion of  the   I/O  operation
    }

    @Override
    public void  failed(Throwable e,  Attachment attach)  {
        // Handle  failure of  the   I/O  operation
    }
}

以下程式碼使用MyHandler範例作為非同步寫操作的完成處理程式。

MyHandler handler = new MyHandler();
ByteBuffer dataBuffer  = get   a  data buffer;
Attachment attach  = new Attachment(); 
attach.asyncChannel = afc; 
attach.buffer = dataBuffer; 
attach.path = path;

// Perform  the   asynchronous write operation 
afc.write(dataBuffer, 0, attach, handler);

以下程式碼演示了如何使用CompletionHandler物件來處理對檔案的非同步寫入的結果。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("test.txt");
    AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE,
        CREATE);
    WriteHandler handler = new WriteHandler();
    ByteBuffer dataBuffer = getDataBuffer();
    Attachment attach = new Attachment();
    attach.asyncChannel = afc;
    attach.buffer = dataBuffer;
    attach.path = path;

    afc.write(dataBuffer, 0, attach, handler);

    System.out.println("Sleeping for 5  seconds...");
    Thread.sleep(5000);
  }
  public static ByteBuffer getDataBuffer() {
    String lineSeparator = System.getProperty("line.separator");
    StringBuilder sb = new StringBuilder();
    sb.append("test");
    sb.append(lineSeparator);
    sb.append("test");
    sb.append(lineSeparator);
    String str = sb.toString();
    Charset cs = Charset.forName("UTF-8");
    ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));
    return bb;
  }
}
class Attachment {
  public Path path;
  public ByteBuffer buffer;
  public AsynchronousFileChannel asyncChannel;
}

class WriteHandler implements CompletionHandler<Integer, Attachment> {
  @Override
  public void completed(Integer result, Attachment attach) {
    System.out.format("%s bytes written  to  %s%n", result,
        attach.path.toAbsolutePath());
    try {
       attach.asyncChannel.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void failed(Throwable e, Attachment attach) {
    try {
      attach.asyncChannel.close();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }
}

範例

以下程式碼演示了如何使用Future物件來處理對檔案的非同步寫入的結果。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Future;

public class Main {
    public static ByteBuffer getDataBuffer() {
        String lineSeparator = System.getProperty("line.separator");

        StringBuilder sb = new StringBuilder();
        sb.append("test");
        sb.append(lineSeparator);

        String str = sb.toString();
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));

        return bb;
    }

    public static void main(String[] args) throws Exception {
        Path path = Paths.get("test.txt");

        try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE, CREATE)) {
            ByteBuffer dataBuffer = getDataBuffer();
            Future<Integer> result = afc.write(dataBuffer, 0);
            while (!result.isDone()) {
                System.out.println("Sleeping for 2  seconds...");
                Thread.sleep(2000);
            }
            int writtenBytes = result.get();
            System.out.format("%s bytes written  to  %s%n", writtenBytes, path.toAbsolutePath());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面的程式碼生成以下結果。

Sleeping for 2  seconds...
6 bytes written  to  F:\website\yiibai\worksp\test.txt

範例-2

以下程式碼演示了如何使用CompletionHandler物件來處理從檔案進行非同步讀取的結果。

import static java.nio.file.StandardOpenOption.READ;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("test.txt");
        AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ);
        ReadHandler handler = new ReadHandler();
        int fileSize = (int) afc.size();
        ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);

        Attachment attach = new Attachment();
        attach.asyncChannel = afc;
        attach.buffer = dataBuffer;
        attach.path = path;

        afc.read(dataBuffer, 0, attach, handler);

        System.out.println("Sleeping for 5  seconds...");
        Thread.sleep(5000);
    }
}

class Attachment {
    public Path path;
    public ByteBuffer buffer;
    public AsynchronousFileChannel asyncChannel;
}

class ReadHandler implements CompletionHandler<Integer, Attachment> {
    @Override
    public void completed(Integer result, Attachment attach) {
        System.out.format("%s bytes read   from  %s%n", result, attach.path);
        System.out.format("Read data is:%n");
        byte[] byteData = attach.buffer.array();
        Charset cs = Charset.forName("UTF-8");
        String data = new String(byteData, cs);
        System.out.println(data);
        try {
            // Close the channel
            attach.asyncChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void failed(Throwable e, Attachment attach) {
        System.out.format("Read operation  on  %s  file failed." + "The  error is: %s%n", attach.path, e.getMessage());
        try {
            // Close the channel
            attach.asyncChannel.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

上面的程式碼生成以下結果。

Sleeping for 5  seconds...
13 bytes read   from  test.txt
Read data is:
test
test2

範例-3

以下程式碼顯示了如何使用Future物件來處理從檔案進行非同步讀取的結果。它使用等待方法(Future.get()方法呼叫)等待非同步檔案I/O完成。

import static java.nio.file.StandardOpenOption.READ;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("test.txt");

        try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ)) {
            int fileSize = (int) afc.size();
            ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);

            Future<Integer> result = afc.read(dataBuffer, 0);
            int readBytes = result.get();

            System.out.format("%s bytes read   from  %s%n", readBytes, path);
            System.out.format("Read data is:%n");

            byte[] byteData = dataBuffer.array();
            Charset cs = Charset.forName("UTF-8");
            String data = new String(byteData, cs);

            System.out.println(data);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

上面的程式碼生成以下結果。

13 bytes read   from  test.txt
Read data is:
test
test2