简体   繁体   English

如何将CompletableFuture.supplyAsync与PriorityBlockingQueue一起使用?

[英]How do I use CompletableFuture.supplyAsync together with PriorityBlockingQueue?

I'm trying to add a priority queue to an existing application that uses ThreadPoolExecutor with a LinkedBlockingQueue via CompletableFuture.supplyAsync. 我正在尝试通过CompletableFuture.supplyAsync将优先级队列添加到使用ThreadPoolExecutor和LinkedBlockingQueue的现有应用程序。 The problem is that I can't come up with a design to assign task priorities that I can then access in PriorityBlockingQueue's Comparator. 问题是我无法想出一个设计来分配任务优先级,然后我可以在PriorityBlockingQueue的Comparator中访问。 That is because my task gets wrapped up by CompletableFuture into an instance of a private inner class called AsyncSupply that hides the original task in a private field. 这是因为我的任务被CompletableFuture包装到一个名为AsyncSupply的私有内部类的实例中,该实例隐藏了私有字段中的原始任务。 The Comparator then gets called with these AsyncSupply objects casted as Runnables, as follows: 然后使用这些AsteSupply对象作为Runnables调用Comparator,如下所示:

public class PriorityComparator<T extends Runnable> implements Comparator<T> {

    @Override
    public int compare(T o1, T o2) {

        // T is an AsyncSupply object.
        // BUT I WANT SOMETHING I CAN ASSIGN PRIORITIES TOO!
        return 0;
    }
}

I investigated the possibility of extending CompletableFuture so I can wrap it in a different object but so much of much of CompletableFuture is encapsulated and uninheritable. 我调查了扩展CompletableFuture的可能性,因此我可以将它包装在一个不同的对象中,但很多CompletableFuture被封装且不可用。 So extending it doesn't seem like an option. 因此,扩展它似乎不是一种选择。 Nor is encapsulating it withing an adapter, as it implements a very wide interface. 也没有用适配器封装它,因为它实现了非常宽的接口。

I'm not sure how to approach this problem aside from copying the entire CompletableFuture, and modifying it. 除了复制整个CompletableFuture并修改它之外,我不确定如何解决这个问题。 Any ideas? 有任何想法吗?

It seems like a limitation in the API that CompletableFuture doesn't provide a straightforward way to use PriorityBlockingQueue . 似乎是API中的限制, CompletableFuture不提供使用PriorityBlockingQueue的简单方法。 Fortunately, we can hack around it without too much trouble. 幸运的是,我们可以毫不费力地破解它。 In Oracle's 1.8 JVM, they happen to name all of the inner classes' fields fn , so extracting our priority-aware Runnable s can be done without too much trouble: 在Oracle的1.8 JVM中,它们恰好命名了所有内部类的字段fn ,因此可以毫不费力地提取我们的优先级感知的Runnable

public class CFRunnableComparator implements Comparator<Runnable> {

    @Override
    @SuppressWarnings("unchecked")
    public int compare(Runnable r1, Runnable r2) {
        // T might be AsyncSupply, UniApply, etc., but we want to
        // compare our original Runnables.
        return ((Comparable) unwrap(r1)).compareTo(unwrap(r2));
    }

    private Object unwrap(Runnable r) {
        try {
            Field field = r.getClass().getDeclaredField("fn");
            field.setAccessible(true);
            // NB: For performance-intensive contexts, you may want to
            // cache these in a ConcurrentHashMap<Class<?>, Field>.
            return field.get(r);
        } catch (IllegalAccessException | NoSuchFieldException e) {
            throw new IllegalArgumentException("Couldn't unwrap " + r, e);
        }
    }
}

This assumes that your Supplier class is Comparable , something like: 这假定您的Supplier类别是Comparable ,例如:

public interface WithPriority extends Comparable<WithPriority> {
    int priority();
    @Override
    default int compareTo(WithPriority o) {
        // Reverse comparison so higher priority comes first.
        return Integer.compare(o.priority(), priority());
    }
}

public class PrioritySupplier<T> implements Supplier<T>, WithPriority {
    private final int priority;
    private final Supplier<T> supplier;
    public PrioritySupplier(int priority, Supplier<T> supplier) {
        this.priority = priority;
        this.supplier = supplier;
    }
    @Override
    public T get() {
        return supplier.get();
    }
    @Override
    public int priority() {
        return priority;
    }
}

Used as follows: 使用如下:

PriorityBlockingQueue<Runnable> q = new PriorityBlockingQueue<>(11 /*default*/,
        new CFRunnableComparator());
ThreadPoolExecutor pool = new ThreadPoolExecutor(..., q);
CompletableFuture.supplyAsync(new PrioritySupplier<>(n, () -> {
    ...
}), pool);

If you create classes like PriorityFunction and PriorityBiConsumer , you can use this same technique to call methods like thenApplyAsync and whenCompleteAsync with appropriate priorities as well. 如果您创建了诸如PriorityFunctionPriorityBiConsumer类的类,则可以使用相同的技术来调用thenApplyAsyncwhenCompleteAsync等方法以及适当的优先级。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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