“单个 LIFO 执行器” / 摇摆工人

发布于 2024-10-02 02:16:03 字数 1233 浏览 10 评论 0原文

考虑一个带有 JList 或 JTable 的 Swing 应用程序,当选择更改时,SwingWorker 会启动并从数据库加载相关数据并更新 UI。这工作正常并且用户界面响应灵敏。

但是,如果用户快速更改所选行(按住向上/向下键),我想确保最后选定的行是最后加载的行,而且我也不想徒劳地查询数据库。所以我想要的是一个单线程执行器,其后进先出队列的大小=1。因此,向其提交任务会删除所有先前提交的任务,并使其一次最多执行 1 个任务,并且最多有 1 个任务等待执行。

我在 java.util.concurrent 中找不到类似的东西,所以我编写了自己的执行器。我这样做是对的还是我从并发包中遗漏了一些东西?该解决方案是否可以接受或者是否有更好的方法来实现我想要的?

public class SingleLIFOExecutor implements Executor
{
    private final ThreadPoolExecutor executor;
    private Runnable lastCommand;

    public SingleLIFOExecutor()
    {
        executor = new ThreadPoolExecutor(0, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
    }

    @Override
    public void execute(Runnable command)
    {
        executor.remove(lastCommand);
        lastCommand = command;
        executor.execute(command);
    }
}

这是一个展示如何使用它的示例:

final Executor executor = new SingleLIFOExecutor();
JList jList = createMyList();
jList.addListSelectionListener(new ListSelectionListener()
{
    @Override
    public void valueChanged(ListSelectionEvent e)
    {
        if (!e.getValueIsAdjusting())
        {
            executor.execute(new MyWorker());
        }
    }
});

Consider a Swing application with a JList or JTable, when the selection changes a SwingWorker is started and loads related data from database and updates UI. This works fine and the UI is responsive.

But if the user is quickly changing the selected row (holding key-up/down) I want to be sure that the last selected row is the one that is loaded last, and also I don't wanna query the DB in vain. So what I want is an single threaded Executor with a LIFO queue of size=1. So submitting a task to it removes any previous submitted tasks and making it execute at most 1 task at a time and having at most 1 task waiting for execution.

I couldn't find anything like this in java.util.concurrent so I wrote my own Executor. Was I right in doing that or am I missing something from the concurrent package? Is the solution acceptable or is there better ways of achieving what I want?

public class SingleLIFOExecutor implements Executor
{
    private final ThreadPoolExecutor executor;
    private Runnable lastCommand;

    public SingleLIFOExecutor()
    {
        executor = new ThreadPoolExecutor(0, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
    }

    @Override
    public void execute(Runnable command)
    {
        executor.remove(lastCommand);
        lastCommand = command;
        executor.execute(command);
    }
}

And here's an example showing how it could be used:

final Executor executor = new SingleLIFOExecutor();
JList jList = createMyList();
jList.addListSelectionListener(new ListSelectionListener()
{
    @Override
    public void valueChanged(ListSelectionEvent e)
    {
        if (!e.getValueIsAdjusting())
        {
            executor.execute(new MyWorker());
        }
    }
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

辞别 2024-10-09 02:16:03

LinkedBlockingDeque 似乎仍然使用带有 ThreadPoolExecutor 的队列。

因此,我使用了一个包装器并将其与 ThreadPoolExecutor 一起使用:

package util;

import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

/**
 * LIFO BlockingQueue to be used with the ExecutorService.
 * @author Daniel
 * @param <T>
 */
public class LinkedBlockingStack<T> implements BlockingQueue<T>{
    private final LinkedBlockingDeque<T> stack = new LinkedBlockingDeque<T>();

    @Override
    public T remove() {
        return stack.remove();
    }

    @Override
    public T poll() {
        return stack.poll();
    }

    @Override
    public T element() {
        return stack.element();
    }

    @Override
    public T peek() {
        return stack.peek();
    }

    @Override
    public int size() {
        return stack.size();
    }

    @Override
    public boolean isEmpty() {
        return stack.isEmpty();
    }

    @Override
    public Iterator<T> iterator() {
        return stack.iterator();
    }

    @Override
    public Object[] toArray() {
        return stack.toArray();
    }

    @Override
    public <S> S[] toArray(final S[] a) {
        return stack.toArray(a);
    }

    @Override
    public boolean containsAll(final Collection<?> c) {
        return stack.containsAll(c);
    }

    @Override
    public boolean addAll(final Collection<? extends T> c) {
        return stack.addAll(c);
    }

    @Override
    public boolean removeAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public boolean retainAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public void clear() {
        stack.clear();
    }

    @Override
    public boolean add(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of add.
    }

    @Override
    public boolean offer(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of offer.
    }

    @Override
    public void put(final T e) throws InterruptedException {
        stack.put(e);
    }

    @Override
    public boolean offer(final T e, final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.offerLast(e, timeout, unit);
    }

    @Override
    public T take() throws InterruptedException {
        return stack.take();
    }

    @Override
    public T poll(final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.poll();
    }

    @Override
    public int remainingCapacity() {
        return stack.remainingCapacity();
    }

    @Override
    public boolean remove(final Object o) {
        return stack.remove(o);
    }

    @Override
    public boolean contains(final Object o) {
        return stack.contains(o);
    }

    @Override
    public int drainTo(final Collection<? super T> c) {
        return stack.drainTo(c);
    }

    @Override
    public int drainTo(final Collection<? super T> c, final int maxElements) {
        return stack.drainTo(c, maxElements);
    }
}

LinkedBlockingDeque seems to still use Queues with ThreadPoolExecutor.

So instead I used a wrapper and used it with the ThreadPoolExecutor:

package util;

import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

/**
 * LIFO BlockingQueue to be used with the ExecutorService.
 * @author Daniel
 * @param <T>
 */
public class LinkedBlockingStack<T> implements BlockingQueue<T>{
    private final LinkedBlockingDeque<T> stack = new LinkedBlockingDeque<T>();

    @Override
    public T remove() {
        return stack.remove();
    }

    @Override
    public T poll() {
        return stack.poll();
    }

    @Override
    public T element() {
        return stack.element();
    }

    @Override
    public T peek() {
        return stack.peek();
    }

    @Override
    public int size() {
        return stack.size();
    }

    @Override
    public boolean isEmpty() {
        return stack.isEmpty();
    }

    @Override
    public Iterator<T> iterator() {
        return stack.iterator();
    }

    @Override
    public Object[] toArray() {
        return stack.toArray();
    }

    @Override
    public <S> S[] toArray(final S[] a) {
        return stack.toArray(a);
    }

    @Override
    public boolean containsAll(final Collection<?> c) {
        return stack.containsAll(c);
    }

    @Override
    public boolean addAll(final Collection<? extends T> c) {
        return stack.addAll(c);
    }

    @Override
    public boolean removeAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public boolean retainAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public void clear() {
        stack.clear();
    }

    @Override
    public boolean add(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of add.
    }

    @Override
    public boolean offer(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of offer.
    }

    @Override
    public void put(final T e) throws InterruptedException {
        stack.put(e);
    }

    @Override
    public boolean offer(final T e, final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.offerLast(e, timeout, unit);
    }

    @Override
    public T take() throws InterruptedException {
        return stack.take();
    }

    @Override
    public T poll(final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.poll();
    }

    @Override
    public int remainingCapacity() {
        return stack.remainingCapacity();
    }

    @Override
    public boolean remove(final Object o) {
        return stack.remove(o);
    }

    @Override
    public boolean contains(final Object o) {
        return stack.contains(o);
    }

    @Override
    public int drainTo(final Collection<? super T> c) {
        return stack.drainTo(c);
    }

    @Override
    public int drainTo(final Collection<? super T> c, final int maxElements) {
        return stack.drainTo(c, maxElements);
    }
}
沒落の蓅哖 2024-10-09 02:16:03

BlockingDeque 我相信这就是你想要的。它支持堆栈。

我的代码中有什么:

private transient final ExecutorService threadPool= 
     new ThreadPoolExecutor(3, 10,10, 
                            TimeUnit.MILLISECONDS, 
                            new LinkedBlockingDeque<Runnable>());

BlockingDeque I believe is what you want. It supports stacks.

What I have in my code:

private transient final ExecutorService threadPool= 
     new ThreadPoolExecutor(3, 10,10, 
                            TimeUnit.MILLISECONDS, 
                            new LinkedBlockingDeque<Runnable>());
紧拥背影 2024-10-09 02:16:03

这是我实施的解决方案,对于我试图解决的问题非常有效:)

/**
 * A "Single Last-In-First-Out Executor".
 * <p>
 * It maintains a queue of <b>one</b> task and only one task may execute simultaneously,
 * submitting a new task to {@link #execute(Runnable)} will discard any previous submitted not yet started tasks.
 */
public class SingleLIFOExecutor implements Executor
{
    private final ThreadPoolExecutor executor;
    private Runnable lastCommand;

    public SingleLIFOExecutor()
    {
        executor = new ThreadPoolExecutor(0, 1, 0, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
    }

    /**
     * @see java.util.concurrent.Executor#execute(java.lang.Runnable)
     */
    @Override
    public void execute(Runnable command)
    {
        executor.remove(lastCommand);
        lastCommand = command;
        executor.execute(command);
    }
}

This was the solution I implemented, works great for the problem I tried to solve :)

/**
 * A "Single Last-In-First-Out Executor".
 * <p>
 * It maintains a queue of <b>one</b> task and only one task may execute simultaneously,
 * submitting a new task to {@link #execute(Runnable)} will discard any previous submitted not yet started tasks.
 */
public class SingleLIFOExecutor implements Executor
{
    private final ThreadPoolExecutor executor;
    private Runnable lastCommand;

    public SingleLIFOExecutor()
    {
        executor = new ThreadPoolExecutor(0, 1, 0, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
    }

    /**
     * @see java.util.concurrent.Executor#execute(java.lang.Runnable)
     */
    @Override
    public void execute(Runnable command)
    {
        executor.remove(lastCommand);
        lastCommand = command;
        executor.execute(command);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文