Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Swing和并发-在操作发生之前休眠请求_Java_Multithreading_Swing_Sleep_Event Dispatch Thread - Fatal编程技术网

Java Swing和并发-在操作发生之前休眠请求

Java Swing和并发-在操作发生之前休眠请求,java,multithreading,swing,sleep,event-dispatch-thread,Java,Multithreading,Swing,Sleep,Event Dispatch Thread,我正在尝试开发一种方法,在最短的时间内安排一个可运行的。 代码应该从发出请求开始,并倒计时,直到经过一段时间,然后执行Runnable。 但我还需要可以发出多个请求,对于每个新请求,延迟将在执行Runnable之前更新 目标是实现以下行为: 当用户滚动JList时,JList的JScrollPane垂直滚动条中的调整侦听器将在执行Runnable之前请求延迟。 每次用户滚动时,都会发出一个新的请求,因此延迟会被延长。 请求立即返回,以便EDT被阻止的时间最少。 因此,Runnable的等待和执行

我正在尝试开发一种方法,在最短的时间内安排一个
可运行的
。 代码应该从发出请求开始,并倒计时,直到经过一段时间,然后执行
Runnable
。 但我还需要可以发出多个请求,对于每个新请求,延迟将在执行
Runnable
之前更新

目标是实现以下行为: 当用户滚动
JList
时,
JList
JScrollPane
垂直滚动条中的调整侦听器将在执行
Runnable
之前请求延迟。 每次用户滚动时,都会发出一个新的请求,因此延迟会被延长。 请求立即返回,以便EDT被阻止的时间最少。 因此,
Runnable
的等待和执行应该发生在不同的
线程
(而不是EDT)中。 从上次发出的请求经过最短时间后,执行
Runnable

我需要这种行为,因为
JList
将包含数千个图像缩略图。 我不想预加载
JList
中的所有缩略图,因为它们可能无法放入内存。 我也不想在用户滚动时加载缩略图,因为他可以任意快速滚动让我把它放进去。 因此,我只想在用户在
JList
中的单个位置等待/停留一段时间(例如500毫秒、1秒或介于两者之间)后开始加载缩略图

我尝试的是用worker
Thread
s创建一个完全手工制作的调度器。 按照我的努力,在评论中有相关解释:

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;

public class SleepThenActScheduler {

    public class WorkerThread extends Thread {

        //How long will we be waiting:
        private final TimeUnit sleepUnit;
        private final long sleepAmount;

        public WorkerThread(final TimeUnit sleepUnit,
                            final long sleepAmount) {
            this.sleepUnit = sleepUnit;
            this.sleepAmount = sleepAmount;
        }

        public TimeUnit getSleepUnit() {
            return sleepUnit;
        }

        public long getSleepAmount() {
            return sleepAmount;
        }

        @Override
        public void run() {
            try {
                if (sleepUnit != null)
                    sleepUnit.sleep(sleepAmount); //Wait for the specified time.
                synchronized (SleepThenActScheduler.this) {
                    if (t == this && whenDone != null) { //If we are the last request:
                        //Execute the "Runnable" in this worker thread:
                        whenDone.accept(System.currentTimeMillis() - start);
                        //Mark the operation as completed:
                        whenDone = null;
                        t = null;
                    }
                }
            }
            catch (final InterruptedException ix) {
                //If interrupted while sleeping, simply do nothing and terminate.
            }
        }
    }

    private LongConsumer whenDone; //This is the "Runnable" to execute after the time has elapsed.
    private WorkerThread t; //This is the last active thread.
    private long start; //This is the start time of the first request made.

    public SleepThenActScheduler() {
        whenDone = null;
        t = null;
        start = 0; //This value does not matter.
    }

    public synchronized void request(final TimeUnit sleepUnit,
                                     final long sleepAmount,
                                     final LongConsumer whenDone) {
        this.whenDone = Objects.requireNonNull(whenDone); //First perform the validity checks and then continue...
        if (t == null) //If this is a first request after the runnable executed, then:
            start = System.currentTimeMillis(); //Log the starting time.
        else //Otherwise we know a worker thread is already running, so:
            t.interrupt(); //stop it.
        t = new WorkerThread(sleepUnit, sleepAmount);
        t.start(); //Start the new worker thread.
    }
}
它的用法将类似于以下代码(如果可能的话,我希望在您可能的答案中保持相关性):

但是该代码为每个请求创建一个新的
线程
(并中断最后一个)。 我不知道这是否是一个好的实践,因此我也尝试使用一个
线程
,该线程循环睡眠,直到从上次发出请求到请求时间过去:

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;

public class SleepThenActThread extends Thread {

    public static class TimeAmount implements Comparable<TimeAmount> {
        private final TimeUnit unit;
        private final long amount;

        public TimeAmount(final TimeUnit unit,
                          final long amount) {
            this.unit = unit;
            this.amount = amount;
        }

        public void sleep() throws InterruptedException {
            /*Warning: does not take into account overflows...
            For example what if we want to sleep for Long.MAX_VALUE days?...
            Look at the implementation of TimeUnit.sleep(...) to see why I am saying this.*/
            if (unit != null)
                unit.sleep(amount);
        }

        public TimeAmount add(final TimeAmount tammt) {
            /*Warning: does not take into account overflows...
            For example what if we want to add Long.MAX_VALUE-1 days with something else?...*/
            return new TimeAmount(TimeUnit.NANOSECONDS, unit.toNanos(amount) + tammt.unit.toNanos(tammt.amount));
        }

        @Override
        public int compareTo(final TimeAmount tammt) {
            /*Warning: does not take into account overflows...
            For example what if we want to compare Long.MAX_VALUE days with something else?...*/
            return Long.compare(unit.toNanos(amount), tammt.unit.toNanos(tammt.amount));
        }
    }

    private static TimeAmount requirePositive(final TimeAmount t) {
        if (t.amount <= 0) //+NullPointerException.
            throw new IllegalArgumentException("Insufficient time amount.");
        return t;
    }

    private LongConsumer runnable;
    private TimeAmount resolution, total;

    public SleepThenActThread(final TimeAmount total,
                              final TimeAmount resolution) {
        this.resolution = requirePositive(resolution);
        this.total = requirePositive(total);
    }

    public synchronized void setResolution(final TimeAmount resolution) {
        this.resolution = requirePositive(resolution);
    }

    public synchronized void setTotal(final TimeAmount total) {
        this.total = requirePositive(total);
    }

    public synchronized void setRunnable(final LongConsumer runnable) {
        this.runnable = Objects.requireNonNull(runnable);
    }

    public synchronized TimeAmount getResolution() {
        return resolution;
    }

    public synchronized TimeAmount getTotal() {
        return total;
    }

    public synchronized LongConsumer getRunnable() {
        return runnable;
    }

    public synchronized void request(final TimeAmount requestedMin,
                                     final LongConsumer runnable) {
        /*In order to achieve requestedMin time to elapse from this last made
        request, we can simply add the requestedMin time to the total time:*/
        setTotal(getTotal().add(requestedMin));
        setRunnable(runnable);
        if (getState().equals(Thread.State.NEW))
            start();
    }

    @Override
    public void run() {
        try {
            final long startMillis = System.currentTimeMillis();
            TimeAmount current = new TimeAmount(TimeUnit.NANOSECONDS, 0);
            while (current.compareTo(getTotal()) < 0) {
                final TimeAmount res = getResolution();
                res.sleep();
                current = current.add(res);
            }
            getRunnable().accept(System.currentTimeMillis() - startMillis);
        }
        catch (final InterruptedException ix) {
        }
    }
}
但是我也不知道这是否是一个好的实践,我想这也会消耗更多的CPU时间

我的问题不是为了最生态的解决方案,而是是否有更好/更正式的方法来实现这一点,同时减少混乱/代码。 例如,我应该使用a、a还是a?但是怎么做呢? 我猜包裹里的东西应该是答案

正如你所能想象的那样,我并不真的在乎延迟的超精确性

评论中关于实现相同目标的其他方法的任何建议也将是好的

我不是真的要求调试,但我也不认为这个问题应该转移到,因为我要求的是一个替代/更好的解决方案

我更希望使用Java8(以及更高版本,如果不可能使用Java8)


谢谢。

这里是一个使用摆动计时器的示例。按下按钮将重新启动2秒延迟

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Delay extends JPanel {
   Timer timer;
   int   presses = 0;

   public Delay() {
      setLayout(new BorderLayout());
      JButton b = new JButton("Sleep 2 seconds");
      JLabel label = new JLabel("The app is currently asleep.");
      add(b, BorderLayout.CENTER);
      add(label, BorderLayout.SOUTH);

      b.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent arg0) {
            timer.restart();
            presses++;
         }
      });

      timer = new Timer(2000, new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            label.setText("Time expired after " + presses + " presses");

         }
      });
      timer.start();
   }

   public static void main(final String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            final JFrame jf = new JFrame();

            JPanel panel = new Delay();
            jf.add(panel);
            jf.pack();
            jf.setVisible(true);
            jf.addWindowListener(new WindowAdapter() {
               @Override
               public void windowClosing(final WindowEvent arg0) {
                  System.exit(0);
               }
            });
         }
      });
   }
}

当发出新请求时,使用java.swing.Timer并调用Timer.restart()。@FredK感谢您的评论。听起来很简单。我不知道这件事,也没想到会这么简单我会测试它。GUI用户是否可以选择一个或多个选项与其他JComponent一起使用,这样他就不必滚动浏览成千上万的图像?@GilbertLeBlanc问题是,用户在从目录加载这些图像后,会一个接一个地对其进行分类。我的意思是,它们不会以任何方式预先分类。如果是的话,我确实可以让他先选择一个类别,然后给他看图片。根据你的评论,一次加载50张左右的图片,让应用程序为进行分类的用户提供一个短暂的休息时间,可能是更好的用户体验。
SleepThenActThread sta = new SleepThenActThread(new TimeAmount(TimeUnit.SECONDS, 1), new TimeAmount(TimeUnit.MILLISECONDS, 10));
final JScrollPane listScroll = new JScrollPane(jlist);
listScroll.getVerticalScrollBar().addAdjustmentListener(adjustmentEvent -> {
    sta.request(new TimeAmount(TimeUnit.SECONDS, 1), actualElapsedTime -> {
        //Code for loading some thumbnails...
    });
});
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Delay extends JPanel {
   Timer timer;
   int   presses = 0;

   public Delay() {
      setLayout(new BorderLayout());
      JButton b = new JButton("Sleep 2 seconds");
      JLabel label = new JLabel("The app is currently asleep.");
      add(b, BorderLayout.CENTER);
      add(label, BorderLayout.SOUTH);

      b.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent arg0) {
            timer.restart();
            presses++;
         }
      });

      timer = new Timer(2000, new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            label.setText("Time expired after " + presses + " presses");

         }
      });
      timer.start();
   }

   public static void main(final String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            final JFrame jf = new JFrame();

            JPanel panel = new Delay();
            jf.add(panel);
            jf.pack();
            jf.setVisible(true);
            jf.addWindowListener(new WindowAdapter() {
               @Override
               public void windowClosing(final WindowEvent arg0) {
                  System.exit(0);
               }
            });
         }
      });
   }
}