简体   繁体   中英

Are `CompletionHandler` and `CompletableFuture` two different approaches for asynchronous programming in Java?

In Java in a Nutshell

The callback style for asynchronous I/O is based on a CompletionHandler , which defines two methods, completed() and failed() , that will be called back when the operation either succeeds or fails. This style is useful if you want immediate notification of events in asynchronous I/O—for example, if there are a large number of I/O operations in flight, but failure of any single operation is not necessarily fatal.

From http://www.deadcoderising.com/java8-writing-asynchronous-code-with-completablefuture/

Beside implementing the Future interface, CompletableFuture also implements the CompletionStage interface.

A CompletionStage is a promise. It promises that the computation eventually will be done.

The great thing about the CompletionStage is that it offers a vast selection of methods that let you attach callbacks that will be executed on completion.

This way we can build systems in a non-blocking fashion.

Both CompletionHandler and CompletableFuture can be used for specifying callback handlers.

What are their relations and differences?

Are CompletionHandler and CompletableFuture two different approaches for asynchronous programming in Java?

Or are they used together?

Thanks.

CompletionHandler<V, A> is the completion interface for NIO asynchronous channels.

It was introduced in Java 7, a few years before Java 8 introduced lambda expressions and casting them to functional interfaces (interfaces with a single abstract method), so it has two methods, completed(V result, A attachment) and failed(Throwable ex, A attachment) , instead of a (now) more comfortable single method.

CompletableFuture<T> is an implementation of the CompletionStage<T> interface.

It was introduced in Java 8, so its static methods to build futures and its instance methods to build continuations use functional interfaces, with which you can comfortably use lambda expressions.

You can wrap every NIO asynchronous call to use a CompletableFuture<T> :

import java.lang.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;

public class AsynchronousCompletionHandler<T> implements CompletionHandler<T, CompletableFuture<T>> {
    public void completed(T result, CompletableFuture<T> attachment) {
        attachment.complete(result);
    }

    public void failed(Throwable ex, CompletableFuture<T> attachment) {
        attachment.completeExceptionally(ex);
    }

    private static final ConcurrentHashMap<Class<?>, AsynchronousCompletionHandler<?>> cache = new ConcurrentHashMap<>();

    static <T> AsynchronousCompletionHandler<T> getInstance(Class<T> clazz) {
        @SuppressWarnings("unchecked")
        AsynchronousCompletionHandler<T> handler = (AsynchronousCompletionHandler<T>)cache.computeIfAbsent(clazz, c -> new AsynchronousCompletionHandler<T>());
        return handler;
    }

    //
    // AsynchronousByteChannel
    public static CompletableFuture<Integer> readAsync(AsynchronousByteChannel channel, ByteBuffer dst) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.read(dst, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }

    public static CompletableFuture<Integer> writeAsync(AsynchronousByteChannel channel, ByteBuffer src) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.write(src, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }

    //
    // AsynchronousFileChannel
    public static CompletableFuture<FileLock> lockAsync(AsynchronousFileChannel channel) {
        CompletableFuture<FileLock> completableFuture = new CompletableFuture<>();
        channel.lock(completableFuture, getInstance(FileLock.class));
        return completableFuture;
    }

    public static CompletableFuture<FileLock> lockAsync(AsynchronousFileChannel channel, long position, long size, boolean shared) {
        CompletableFuture<FileLock> completableFuture = new CompletableFuture<>();
        channel.lock(position, size, shared, completableFuture, getInstance(FileLock.class));
        return completableFuture;
    }

    public static CompletableFuture<Integer> readAsync(AsynchronousFileChannel channel, ByteBuffer dst, long position) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.read(dst, position, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }

    public static CompletableFuture<Integer> writeAsync(AsynchronousFileChannel channel, ByteBuffer src, long position) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.write(src, position, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }

    //
    // AsynchronousServerSocketChannel
    public static CompletableFuture<AsynchronousSocketChannel> acceptAsync(AsynchronousServerSocketChannel channel) {
        CompletableFuture<AsynchronousSocketChannel> completableFuture = new CompletableFuture<>();
        channel.accept(completableFuture, getInstance(AsynchronousSocketChannel.class));
        return completableFuture;
    }

    //
    // AsynchronousSocketChannel
    public static CompletableFuture<Void> connectAsync(AsynchronousSocketChannel channel, SocketAddress remote) {
        CompletableFuture<Void> completableFuture = new CompletableFuture<>();
        channel.connect(remote, completableFuture, getInstance(Void.class));
        return completableFuture;
    }

    public static CompletableFuture<Long> readAsync(AsynchronousSocketChannel channel, ByteBuffer[] dsts, int offset, int length, long timeout, TimeUnit unit) {
        CompletableFuture<Long> completableFuture = new CompletableFuture<>();
        channel.read(dsts, offset, length, timeout, unit, completableFuture, getInstance(Long.class));
        return completableFuture;
    }

    public static CompletableFuture<Integer> readAsync(AsynchronousSocketChannel channel, ByteBuffer dst) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.read(dst, completableFuture, getInstance(Integer.class));
        return completableFuture;
    } 

    public static CompletableFuture<Integer> readAsync(AsynchronousSocketChannel channel, ByteBuffer dst, long timeout, TimeUnit unit) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.read(dst, timeout, unit, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }

    public static CompletableFuture<Long> writeAsync(AsynchronousSocketChannel channel, ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit) {
        CompletableFuture<Long> completableFuture = new CompletableFuture<>();
        channel.write(srcs, offset, length, timeout, unit, completableFuture, getInstance(Long.class));
        return completableFuture;
    }

    public static CompletableFuture<Integer> writeAsync(AsynchronousSocketChannel channel, ByteBuffer src) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.write(src, completableFuture, getInstance(Integer.class));
        return completableFuture;
    } 

    public static CompletableFuture<Integer> writeAsync(AsynchronousSocketChannel channel, ByteBuffer src, long timeout, TimeUnit unit) {
        CompletableFuture<Integer> completableFuture = new CompletableFuture<>();
        channel.write(src, timeout, unit, completableFuture, getInstance(Integer.class));
        return completableFuture;
    }
}

A sample usage (for exposition only; no error handling, no disposal of resources):

import static AsynchronousCompletionHandler;

AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(5000));
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
acceptAsync(serverChannel)
    .thenCompose(clientChannel -> readAsync(clientChannel, buffer))
    .thenAccept(readBytes -> System.out.format("read %d bytes from client%n", readBytes));

If you look at CompletableFuture (since Java 8), you'll notice that it has a vast amount of functionality allowing a lot more than just callbacks. With chaining, combining and other interesting features.

Compare that to CompletionHandler (since Java 7), and the difference should be obvious.

Nothing prevents you from using both, and it may be even necessary depending on what kind of APIs you're working with, but if you have a chance to use CompletableFuture , you really don't need to tack on a CompletionHandler .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM