如何使用 SwingWorker 模拟缓冲外围设备?

发布于 2024-11-29 08:44:20 字数 3401 浏览 0 评论 0原文

我使用这个练习作为教学工具来帮助我记住一些 Java GUI 编程概念。我正在寻找的是一种总体理解,而不是针对一个特定问题的详细解决方案。我希望这种“正确”的编码能够教会我很多关于如何处理未来的多线程问题的知识。如果这对于这个论坛来说太笼统了,可能它属于程序员?

我正在模拟读卡器。它有一个 GUI,允许我们将卡片加载到料斗中并按“开始”等等,但它的主要“客户端”是 CPU,在单独的线程上运行并请求卡片。

读卡器维护一个缓冲区。如果收到卡片请求并且缓冲区为空,则读卡器必须从卡片盒中读取卡片(需要 1/4 秒,这是 1962 年)。将卡读入缓冲区后,读卡器将缓冲区发送到 CPU,并在下一个请求之前立即启动另一个缓冲区加载操作。

如果不仅缓冲区是空的,而且料斗中也没有卡片,那么我们必须等到操作员将一副牌放入料斗中并按下“开始”(这总是启动缓冲区加载操作)。

在我的实现中,卡请求以在 EDT 上排队的 invokeLater() Runnables 的形式发送到读卡器。在 myRunnable.run() 时,要么缓冲区可用(在这种情况下,我们可以将其发送到 CPU 并启动另一个缓冲区加载操作),要么缓冲区为空。如果是空的怎么办?

有两种可能性:(a) 飞行中已经有缓冲加载操作,或者 (b) 卡片槽为空(或尚未启动)。无论哪种情况,让 EDT 等待都是不可接受的。工作(和等待)必须在后台线程上完成。

为了简单起见,我尝试生成一个 SwingWorker 来响应每个卡请求,无论缓冲区的状态如何。伪代码是:

SwingWorker worker = new SwingWorker<Void, Void>() {
    public Void doInBackground() throws Exception {
        if (buffer.isEmpty()) {
            /*
             * fill() takes 1/4 second (simulated by Thread.sleep)
             * or possibly minutes if we need to have another 
             * card deck mounted by operator.
             */
            buffer.fill();
        }
        Card card = buffer.get(); // empties buffer
        /*
         * Send card to CPU
         */
        CPU.sendMessage(card); // <== (A) put card in msg queue
        /* 
         * Possible race window here!!
         */
        buffer.fill(); //         <== (B) pre-fetch next card
        return null;
    }
};
worker.execute();

这产生了一些奇怪的计时效果 - 我怀疑,由于 buffer.fill() 竞争,可能会发生如下情况:如果在 (A) 和 (B) 之间,CPU收到卡,发送对另一张卡的请求,并代表它生成另一个 SwingWorker 线程,那么可能有两个线程同时尝试填充缓冲区。 [删除 (B) 处的预取调用解决了这个问题。]

所以我认为为每次读取生成一个 SwingWorker 线程是错误的。卡的缓冲和发送必须在单个线程中串行化。该线程必须尝试预取缓冲区,并且如果我们用完卡片并且必须等待将更多卡片放入料斗中,则必须能够等待并恢复。我怀疑 SwingWorker 拥有长时间运行的后台线程来处理这个问题,但我还没有完全做到这一点。

假设采用 SwingWorker 线程,我该如何实现这一点,消除 EDT 上的延迟,允许线程阻塞等待料斗重新填充,并处理缓冲区填充是否在另一个卡请求到达之前或之后完成的不确定性?


编辑:我从另一个线程得到了答案,并将在这里回顾一下:

建议我不要使用 SwingWorker 线程,而是使用 SwingWorker 线程。在开始时创建一个 ExecutorService newSingleThreadExecutor() 一次,并使用 GUI 将冗长的方法加入队列execute(Runnable foo),如下(此代码在 EDT 中运行):

private ExecutorService executorService;
::
/*
 * In constructor: create the thread
 */
executorService = Executors.newSingleThreadExecutor();
::
/*
 * When EDT receives a request for a card it calls readCard(),
 * which queues the work out to the *single* thread.
 */
public void readCard() throws Exception {
    executorService.execute(new Runnable() {
        public void run() {
            if (buffer.isEmpty()) {
                /*
                 * fill() takes 1/4 second (simulated by Thread.sleep)
                 * or possibly minutes if we need to have another 
                 * card deck mounted by operator.
                 */
                buffer.fill();
            }
            Card card = buffer.get(); // empties buffer
            /*
             * Send card to CPU
             */
            CPU.sendMessage(card); // <== (A) put card in msg queue
            /* 
             * No race!  Next request will run on same thread, after us.
             */
            buffer.fill(); //         <== (B) pre-fetch next card
            return;
        }
    });
}

这与 SwingWorker 之间的主要区别在于,这确保只有一个工作线程。

I'm using this exercise as a pedagogical tool to help me burn in some Java GUI programming concepts. What I'm looking for is a general understanding, rather than a detailed solution to one specific problem. I expect that coding this "right" will teach me a lot about how to approach future multi-threaded problems. If this is too general for this forum, possibly it belongs in Programmers?

I'm simulating a card reader. It has a GUI, allowing us to load cards into the hopper and press Start and so forth, but its main "client" is the CPU, running on a separate thread and requesting cards.

The card reader maintains a single buffer. If a card request comes in and the buffer is empty, the card reader must read a card from the hopper (which takes 1/4 of a second, this being 1962). After the card has been read into the buffer, the card reader sends the buffer to the CPU, and immediately initiates another buffer-loading operation, in advance of the next request.

If not only the buffer is empty but there are no cards in the hopper, then we must wait until the operator has placed a deck in the hopper and pressed Start (which always initiates a buffer-load operation).

In my implementation, card requests are sent to the card reader in the form of invokeLater() Runnables being queued on the EDT. At myRunnable.run() time, either a buffer will be available (in which case we can send it to the CPU and kick off another buffer-load operation), or the buffer will be empty. What if it's empty?

Two possibilities: (a) there's already a buffer-load operation in flight, or (b) the card hopper is empty (or hasn't been started). In either case, it's not acceptable to keep the EDT waiting. The work (and the waiting) must be done on a background thread.

For the sake of simplicity, I tried spawning a SwingWorker in response to every card request, regardless of the status of the buffer. The pseudocode was:

SwingWorker worker = new SwingWorker<Void, Void>() {
    public Void doInBackground() throws Exception {
        if (buffer.isEmpty()) {
            /*
             * fill() takes 1/4 second (simulated by Thread.sleep)
             * or possibly minutes if we need to have another 
             * card deck mounted by operator.
             */
            buffer.fill();
        }
        Card card = buffer.get(); // empties buffer
        /*
         * Send card to CPU
         */
        CPU.sendMessage(card); // <== (A) put card in msg queue
        /* 
         * Possible race window here!!
         */
        buffer.fill(); //         <== (B) pre-fetch next card
        return null;
    }
};
worker.execute();

This produced some odd timing effects - due, I suspect, to a buffer.fill() race that could occur as follows: if, between (A) and (B), the CPU received the card, sent a request for another one, and had another SwingWorker thread spawned on its behalf, then there might be two threads simultaneously trying to fill the buffer. [Removing the pre-fetch call at (B) solved that.]

So I think spawning a SwingWorker thread for every read is wrong. The buffering and sending of cards must be serialized in a single thread. That thread must attempt to pre-fetch a buffer, and must be able to wait and resume if we run out of cards and have to wait for more to be placed in the hopper. I suspect that SwingWorker has what is required to be a long-running background thread to handle this, but I'm not quite there yet.

Assuming a SwingWorker thread is the way to go, how might I implement this, eliminating delay on the EDT, allowing the thread to block awaiting a hopper refill, and handling the uncertainty of whether buffer-filling completes before or after another card request arrives?


EDIT: I got an answer from another thread and will recap it here:

Instead of using a SwingWorker thread, it was recommended I create an ExecutorService newSingleThreadExecutor() once, at the beginning, and have the GUI enqueue lengthy methods on it using execute(Runnable foo), as follows (this code runs in the EDT):

private ExecutorService executorService;
::
/*
 * In constructor: create the thread
 */
executorService = Executors.newSingleThreadExecutor();
::
/*
 * When EDT receives a request for a card it calls readCard(),
 * which queues the work out to the *single* thread.
 */
public void readCard() throws Exception {
    executorService.execute(new Runnable() {
        public void run() {
            if (buffer.isEmpty()) {
                /*
                 * fill() takes 1/4 second (simulated by Thread.sleep)
                 * or possibly minutes if we need to have another 
                 * card deck mounted by operator.
                 */
                buffer.fill();
            }
            Card card = buffer.get(); // empties buffer
            /*
             * Send card to CPU
             */
            CPU.sendMessage(card); // <== (A) put card in msg queue
            /* 
             * No race!  Next request will run on same thread, after us.
             */
            buffer.fill(); //         <== (B) pre-fetch next card
            return;
        }
    });
}

The main difference between this and SwingWorker is that this ensures there's only one worker thread.

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

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

发布评论

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

评论(3

残花月 2024-12-06 08:44:20

了解 SwingWorker使用 ExecutorService 内部;为了方便起见,它增加了临时EDT处理机制。只要您在 EDT 上更新 GUI 并同步对任何共享数据的访问,后者就等同于前者。

假设您使用 模型–视图–控制器 模式,建议 这里,你的模型就是操作一个CPU的。尽管它可能是不同的类,但我看不出有任何理由在不同的线程上对读卡器进行建模。相反,让处理器模型有一个读卡器模型来等待 java.util.Timer 线程,在计时器触发时更新模型。让更新的模型在将事件发布到 EDT 的正常过程中通知视图。让控制器取消并调度读卡器模型以响应查看手势。

It may help to know that SwingWorker uses an ExecutorService internally; it adds the interim EDT processing mechanism for convenience. As long as you update your GUI on the EDT and synchronize access to any shared data, the latter is equivalent to the former.

Assuming you are using the Model–View–Controller pattern, suggested here, your model is the operation of a cpu. Although it may be a different class, I can't see any reason to model the card reader on a different thread. Instead, let the processor model have a card reader model that does the waiting on a java.util.Timer thread, updating the model as the timer fires. Let the updated model notify the view in the normal course of posting events to the EDT. Let the controller cancel and schedule the card reader model in response to view gestures.

半夏半凉 2024-12-06 08:44:20

我附加到原始问题的“答案”中缺少一件事:

我将耗时的工作(只不过是用于教学目的的 Thread.sleep() )交给了一个后台线程,通过单线程执行器。然而,出现了一个问题,因为后台线程通过 poll() 对充当 Swing 组件数据模型的 List 进行“读卡”,并引发大量 AWT 数组索引超出范围异常。在多次尝试同步 EDT 和我的后台线程对列表的访问但徒劳之后,我将命令包装到 poll() 列表并在一个小的 Runnable() 中更新 GUI,并使用 invokeAndWait() 来导致当我的后台任务等待时,它们会在 EDT 上运行。

这是我修改后的解决方案:

private ExecutorService executorService;
 :
executorService = Executors.newSingleThreadExecutor();
 :
/*
 * When EDT receives a request for a card it calls readCard(),
 * which queues the work to the *single* thread.
 */
public void readCard() throws Exception {
    executorService.execute(new Runnable() {
        public void run() {
            if (buffer.isEmpty()) {
                /*
                 * fill() takes 1/4 second (simulated by Thread.sleep)
                 */
                buffer.fill();
            }
            Card card = buffer.get(); // empties buffer
            /*
             * Send card to CPU
             */
            CPU.sendMessage(card); // <== (A) put card in msg queue
            /* 
             * No race!  Next request will run on same thread, after us.
             */
            buffer.fill(); //         <== (B) pre-fetch next card
            return;
        }
    });
}

/*
 * IMPORTANT MODIFICATION HERE - - -
 *
 * buffer fill() method has to remove item from the list that is the
 * model behind a JList - only safe way is to do that on EDT!
 */
private void fill() {
    SwingUtilities.invokeAndWait(new Runnable() {
        /*
         * Running here on the EDT
         */
        public void run() {
            /*
             * Hopper not empty, so we will be able to read a card.
             */
            buffer = readHopper.pollLast();  // read next card from current deck
            fireIntervalRemoved(this, readHopper.size(), readHopper.size()); 
            gui.viewBottomOfHopper(); // scroll read hopper view correctly
        }
    });
    // back to my worker thread, to do 1/4 sec. of heavy number crunching ;)
    // while leaving the GUI responsive 
    Thread.sleep(250);
     :
    etc.
}

There was one thing missing from the "answer" I had appended to the original question:

I was handing off the time-consuming work (nothing more than a Thread.sleep() for pedagogical purposes) to a background thread, via a Single Thread Executor. A problem arose, however, because the background thread was "reading a card" by poll()ing the List that was serving as the data model for a Swing component, and raising lots of AWT array index out of range exceptions. After several futile attempts to synchronize access to the List by both the EDT and my background thread, I punted, and wrapped the commands to poll() the List and update the GUI in a small Runnable(), and used invokeAndWait() to cause them to run on the EDT while my background task waited.

Here's my revised solution:

private ExecutorService executorService;
 :
executorService = Executors.newSingleThreadExecutor();
 :
/*
 * When EDT receives a request for a card it calls readCard(),
 * which queues the work to the *single* thread.
 */
public void readCard() throws Exception {
    executorService.execute(new Runnable() {
        public void run() {
            if (buffer.isEmpty()) {
                /*
                 * fill() takes 1/4 second (simulated by Thread.sleep)
                 */
                buffer.fill();
            }
            Card card = buffer.get(); // empties buffer
            /*
             * Send card to CPU
             */
            CPU.sendMessage(card); // <== (A) put card in msg queue
            /* 
             * No race!  Next request will run on same thread, after us.
             */
            buffer.fill(); //         <== (B) pre-fetch next card
            return;
        }
    });
}

/*
 * IMPORTANT MODIFICATION HERE - - -
 *
 * buffer fill() method has to remove item from the list that is the
 * model behind a JList - only safe way is to do that on EDT!
 */
private void fill() {
    SwingUtilities.invokeAndWait(new Runnable() {
        /*
         * Running here on the EDT
         */
        public void run() {
            /*
             * Hopper not empty, so we will be able to read a card.
             */
            buffer = readHopper.pollLast();  // read next card from current deck
            fireIntervalRemoved(this, readHopper.size(), readHopper.size()); 
            gui.viewBottomOfHopper(); // scroll read hopper view correctly
        }
    });
    // back to my worker thread, to do 1/4 sec. of heavy number crunching ;)
    // while leaving the GUI responsive 
    Thread.sleep(250);
     :
    etc.
}
小瓶盖 2024-12-06 08:44:20

1) 创建 GUI,应该为空,或者基于 Java 包中的默认值

2) start periodic = new AccurateScheduledRunnable() {...};

    periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod,
          TimeUnit.MINUTES);

3) 声明 的监视器预定的未来周期监视器;
然后你会得到例如...剩余时间

periodic = new AccurateScheduledRunnable() {...};

long she = periodicMonitor.getDelay(TimeUnit.SECONDS); 

4) SwingWorker 可以通过使用支持多线程
Executor executor = Executors.newCachedThreadPool();,那么你就可以这个

5)无论你期望什么......

编辑

嗯嗯AccurateScheduledRunnable 是自定义抽象类

,但为了我的享受,我构建了这个类,..给出我谈论的答案

在此处输入图像描述

import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.table.*;

public class TableIcon extends JFrame implements Runnable {

    private static final long serialVersionUID = 1L;
    private JTable table;
    private JLabel myLabel = new JLabel("waiting");
    private JLabel lastRunLabel = new JLabel("waiting");
    private int pHeight = 40;
    private boolean runProcess = true;
    private int count = 0;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private ScheduledExecutorService scheduler;
    private AccurateScheduledRunnable periodic;
    private ScheduledFuture<?> periodicMonitor;
    private Executor executor = Executors.newCachedThreadPool();
    private Date dateLast;
    private Date dateNext;
    private Date dateRun;
    private int taskPeriod = 1;
    private int dayCount = 0;
    private int hourCount = 0;
    private int minuteCount = 0;
    private int secondCount = 0;
    private Timer timerRun;
    private int delay = 3000;
    private boolean bolo = false;

    public TableIcon() {
        ImageIcon errorIcon = (ImageIcon) UIManager.getIcon("OptionPane.errorIcon");
        ImageIcon infoIcon = (ImageIcon) UIManager.getIcon("OptionPane.informationIcon");
        ImageIcon warnIcon = (ImageIcon) UIManager.getIcon("OptionPane.warningIcon");
        String[] columnNames = {"Picture", "Description"};
        Object[][] data = {{errorIcon, "About"}, {infoIcon, "Add"}, {warnIcon, "Copy"},};
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {

            private static final long serialVersionUID = 1L;

            @Override
            public Class getColumnClass(int column) {
                return getValueAt(0, column).getClass();
            }
        };
        table = new JTable(model);
        table.setRowHeight(pHeight);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
        lastRunLabel.setPreferredSize(new Dimension(200, pHeight));
        lastRunLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(lastRunLabel, BorderLayout.NORTH);
        myLabel.setPreferredSize(new Dimension(200, pHeight));
        myLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(myLabel, BorderLayout.SOUTH);
        scheduler = Executors.newSingleThreadScheduledExecutor();
        periodic = new AccurateScheduledRunnable() {

            private final int ALLOWED_TARDINESS = 200;
            private int countRun = 0;
            private int countCalled = 0;

            @Override
            public void run() {
                countCalled++;
                if (this.getExecutionTime() < ALLOWED_TARDINESS) {
                    countRun++;
                    executor.execute(new TableIcon.MyTask("GetCurrTime")); // non on EDT
                }
            }
        };
        periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.MINUTES);
        periodic.setThreadMonitor(periodicMonitor);
        new Thread(this).start();
        prepareStartShedule();
    }

    private void prepareStartShedule() {
        timerRun = new javax.swing.Timer(delay, startCycle());
        timerRun.setRepeats(true);
        timerRun.start();
    }

    private Action startCycle() {
        return new AbstractAction("Start Shedule") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                executor.execute(new TableIcon.MyTask("StartShedule")); // non on EDT
            }
        };
    }

    private void changeTableValues() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                if (bolo) {
                    bolo = false;
                    table.getModel().setValueAt("*/*/*/**/*/*/*", 0, 1);
                    table.getModel().setValueAt(" k k k k k k k k", 1, 1);
                    table.getModel().setValueAt("@#@#@#@", 2, 1);
                } else {
                    bolo = true;
                    table.getModel().setValueAt("Green Peper", 0, 1);
                    table.getModel().setValueAt("Yellow Apple", 1, 1);
                    table.getModel().setValueAt("Orange Bus", 2, 1);
                }
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    private void distAppInfo() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                dateNext = new java.util.Date();
                dateLast = new java.util.Date();
                long tme = dateNext.getTime();
                tme += (taskPeriod * 60) * 1000;
                dateNext.setTime(tme);
                lastRunLabel.setText("Last : " + sdf.format(dateLast) + " / Next : " + sdf.format(dateNext));
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    private void changeLabelColor() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                Color clr = lastRunLabel.getForeground();
                if (clr == Color.red) {
                    lastRunLabel.setForeground(Color.blue);
                } else {
                    lastRunLabel.setForeground(Color.red);
                }
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    @Override
    public void run() {
        while (runProcess) {
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            executor.execute(new TableIcon.MyTask("ChangeIconLabel")); // non on EDT
        }
    }

    private void setIconLabel() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                String text = "";
                dateRun = new java.util.Date();
                long tme = dateRun.getTime();
                long she = periodicMonitor.getDelay(TimeUnit.SECONDS);
                dayCount = (int) (she / (24 * 60 * 60));
                hourCount = (int) (she / (60 * 60));
                minuteCount = (int) (she / (60));
                secondCount = (int) she;
                int hourss = hourCount;
                int minutess = minuteCount;
                if (dayCount > 0) {
                    hourCount -= (dayCount * 24);
                    minuteCount -= ((dayCount * 24 * 60) + (hourCount * 60));
                    secondCount -= (minutess * 60);
                    //System.out.println(" Days : " + dayCount + "  ,Hours : " + hourCount + "  , Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + dayCount + " Days  " + hourCount + " h : " + minuteCount + " m : " + secondCount + " s");
                } else if (hourCount > 0) {
                    minuteCount -= ((hourss * 60));
                    secondCount -= (minutess * 60);
                    //System.out.println(" Hours : " + hourCount + "  , Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + hourCount + " h : " + minuteCount + " m : " + secondCount + " s");
                } else if (minuteCount > 0) {
                    secondCount -= (minutess * 60);
                    //System.out.println(" Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + minuteCount + " m : " + secondCount + " s");
                } else {
                    //System.out.println(" Seconds : " + secondCount);
                    text = ("  " + secondCount + " s");
                }
                tme += she * 1000;
                ImageIcon myIcon = (ImageIcon) table.getModel().getValueAt(count, 0);
                String lbl = "Row at :  " + count + "  Remains : " + text;
                myLabel.setIcon(myIcon);
                myLabel.setText(lbl);
                count++;
                if (count > 2) {
                    count = 0;
                }
            }
        });
    }

    public static void main(String[] args) {
        TableIcon frame = new TableIcon();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setLocation(150, 150);
        frame.pack();
        frame.setVisible(true);
    }

    private class MyTask extends SwingWorker<Void, Integer> {

        private String str;
        private String namePr;

        MyTask(String str) {
            this.str = str;
            addPropertyChangeListener(new SwingWorkerCompletionWaiter(str, namePr));
        }

        @Override
        protected Void doInBackground() throws Exception {
            if (str.equals("GetCurrTime")) {
                distAppInfo();
            } else if (str.equals("ChangeIconLabel")) {
                setIconLabel();
            } else if (str.equals("StartShedule")) {
                changeTableValues();
            }
            return null;
        }

        @Override
        protected void process(List<Integer> progress) {
            //System.out.println(str + " " + progress.get(progress.size() - 1));
        }

        @Override
        protected void done() {
            if (str.equals("GetCurrTime")) {
                changeLabelColor();
            } else if (str.equals("ChangeIconLabel")) {
                //setIconLabel();
            } else if (str.equals("StartShedule")) {
                //changeTableValues();
            }
        }
    }

    private class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private String str;
        private String namePr;

        SwingWorkerCompletionWaiter(String str, String namePr) {
            this.str = str;
            this.namePr = namePr;
        }

        SwingWorkerCompletionWaiter(String namePr) {
            this.namePr = namePr;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else {
                System.out.println("SomeThing Wrong happends with Thread Status with Name :" + str);
            }
        }
    }
}

abstract class AccurateScheduledRunnable implements Runnable {

    private ScheduledFuture<?> thisThreadsMonitor;

    public void setThreadMonitor(ScheduledFuture<?> monitor) {
        this.thisThreadsMonitor = monitor;
    }

    protected long getExecutionTime() {
        long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
        return delay;
    }
}

1) create GUI, should be empty, or based on default values from Java package(s)

2) start periodic = new AccurateScheduledRunnable() {...};

    periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod,
          TimeUnit.MINUTES);

3) declare monitor for ScheduledFuture<?> periodicMonitor;
then you get for example ... remaining time from

periodic = new AccurateScheduledRunnable() {...};

long she = periodicMonitor.getDelay(TimeUnit.SECONDS); 

4) SwingWorker could support multithreading by using
Executor executor = Executors.newCachedThreadPool();, then you can be able this one

5) whatever you expected ...

EDIT

hmmmm AccurateScheduledRunnable is custom Abstract Class

but for my enjoy I built this one, .. give an answer what I talked about

enter image description here

import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.table.*;

public class TableIcon extends JFrame implements Runnable {

    private static final long serialVersionUID = 1L;
    private JTable table;
    private JLabel myLabel = new JLabel("waiting");
    private JLabel lastRunLabel = new JLabel("waiting");
    private int pHeight = 40;
    private boolean runProcess = true;
    private int count = 0;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private ScheduledExecutorService scheduler;
    private AccurateScheduledRunnable periodic;
    private ScheduledFuture<?> periodicMonitor;
    private Executor executor = Executors.newCachedThreadPool();
    private Date dateLast;
    private Date dateNext;
    private Date dateRun;
    private int taskPeriod = 1;
    private int dayCount = 0;
    private int hourCount = 0;
    private int minuteCount = 0;
    private int secondCount = 0;
    private Timer timerRun;
    private int delay = 3000;
    private boolean bolo = false;

    public TableIcon() {
        ImageIcon errorIcon = (ImageIcon) UIManager.getIcon("OptionPane.errorIcon");
        ImageIcon infoIcon = (ImageIcon) UIManager.getIcon("OptionPane.informationIcon");
        ImageIcon warnIcon = (ImageIcon) UIManager.getIcon("OptionPane.warningIcon");
        String[] columnNames = {"Picture", "Description"};
        Object[][] data = {{errorIcon, "About"}, {infoIcon, "Add"}, {warnIcon, "Copy"},};
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {

            private static final long serialVersionUID = 1L;

            @Override
            public Class getColumnClass(int column) {
                return getValueAt(0, column).getClass();
            }
        };
        table = new JTable(model);
        table.setRowHeight(pHeight);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
        lastRunLabel.setPreferredSize(new Dimension(200, pHeight));
        lastRunLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(lastRunLabel, BorderLayout.NORTH);
        myLabel.setPreferredSize(new Dimension(200, pHeight));
        myLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(myLabel, BorderLayout.SOUTH);
        scheduler = Executors.newSingleThreadScheduledExecutor();
        periodic = new AccurateScheduledRunnable() {

            private final int ALLOWED_TARDINESS = 200;
            private int countRun = 0;
            private int countCalled = 0;

            @Override
            public void run() {
                countCalled++;
                if (this.getExecutionTime() < ALLOWED_TARDINESS) {
                    countRun++;
                    executor.execute(new TableIcon.MyTask("GetCurrTime")); // non on EDT
                }
            }
        };
        periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.MINUTES);
        periodic.setThreadMonitor(periodicMonitor);
        new Thread(this).start();
        prepareStartShedule();
    }

    private void prepareStartShedule() {
        timerRun = new javax.swing.Timer(delay, startCycle());
        timerRun.setRepeats(true);
        timerRun.start();
    }

    private Action startCycle() {
        return new AbstractAction("Start Shedule") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                executor.execute(new TableIcon.MyTask("StartShedule")); // non on EDT
            }
        };
    }

    private void changeTableValues() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                if (bolo) {
                    bolo = false;
                    table.getModel().setValueAt("*/*/*/**/*/*/*", 0, 1);
                    table.getModel().setValueAt(" k k k k k k k k", 1, 1);
                    table.getModel().setValueAt("@#@#@#@", 2, 1);
                } else {
                    bolo = true;
                    table.getModel().setValueAt("Green Peper", 0, 1);
                    table.getModel().setValueAt("Yellow Apple", 1, 1);
                    table.getModel().setValueAt("Orange Bus", 2, 1);
                }
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    private void distAppInfo() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                dateNext = new java.util.Date();
                dateLast = new java.util.Date();
                long tme = dateNext.getTime();
                tme += (taskPeriod * 60) * 1000;
                dateNext.setTime(tme);
                lastRunLabel.setText("Last : " + sdf.format(dateLast) + " / Next : " + sdf.format(dateNext));
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    private void changeLabelColor() {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                Color clr = lastRunLabel.getForeground();
                if (clr == Color.red) {
                    lastRunLabel.setForeground(Color.blue);
                } else {
                    lastRunLabel.setForeground(Color.red);
                }
            }
        };
        SwingUtilities.invokeLater(doRun);
    }

    @Override
    public void run() {
        while (runProcess) {
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            executor.execute(new TableIcon.MyTask("ChangeIconLabel")); // non on EDT
        }
    }

    private void setIconLabel() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                String text = "";
                dateRun = new java.util.Date();
                long tme = dateRun.getTime();
                long she = periodicMonitor.getDelay(TimeUnit.SECONDS);
                dayCount = (int) (she / (24 * 60 * 60));
                hourCount = (int) (she / (60 * 60));
                minuteCount = (int) (she / (60));
                secondCount = (int) she;
                int hourss = hourCount;
                int minutess = minuteCount;
                if (dayCount > 0) {
                    hourCount -= (dayCount * 24);
                    minuteCount -= ((dayCount * 24 * 60) + (hourCount * 60));
                    secondCount -= (minutess * 60);
                    //System.out.println(" Days : " + dayCount + "  ,Hours : " + hourCount + "  , Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + dayCount + " Days  " + hourCount + " h : " + minuteCount + " m : " + secondCount + " s");
                } else if (hourCount > 0) {
                    minuteCount -= ((hourss * 60));
                    secondCount -= (minutess * 60);
                    //System.out.println(" Hours : " + hourCount + "  , Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + hourCount + " h : " + minuteCount + " m : " + secondCount + " s");
                } else if (minuteCount > 0) {
                    secondCount -= (minutess * 60);
                    //System.out.println(" Minutes : " + minuteCount + "  , Seconds : " + secondCount);
                    text = ("  " + minuteCount + " m : " + secondCount + " s");
                } else {
                    //System.out.println(" Seconds : " + secondCount);
                    text = ("  " + secondCount + " s");
                }
                tme += she * 1000;
                ImageIcon myIcon = (ImageIcon) table.getModel().getValueAt(count, 0);
                String lbl = "Row at :  " + count + "  Remains : " + text;
                myLabel.setIcon(myIcon);
                myLabel.setText(lbl);
                count++;
                if (count > 2) {
                    count = 0;
                }
            }
        });
    }

    public static void main(String[] args) {
        TableIcon frame = new TableIcon();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setLocation(150, 150);
        frame.pack();
        frame.setVisible(true);
    }

    private class MyTask extends SwingWorker<Void, Integer> {

        private String str;
        private String namePr;

        MyTask(String str) {
            this.str = str;
            addPropertyChangeListener(new SwingWorkerCompletionWaiter(str, namePr));
        }

        @Override
        protected Void doInBackground() throws Exception {
            if (str.equals("GetCurrTime")) {
                distAppInfo();
            } else if (str.equals("ChangeIconLabel")) {
                setIconLabel();
            } else if (str.equals("StartShedule")) {
                changeTableValues();
            }
            return null;
        }

        @Override
        protected void process(List<Integer> progress) {
            //System.out.println(str + " " + progress.get(progress.size() - 1));
        }

        @Override
        protected void done() {
            if (str.equals("GetCurrTime")) {
                changeLabelColor();
            } else if (str.equals("ChangeIconLabel")) {
                //setIconLabel();
            } else if (str.equals("StartShedule")) {
                //changeTableValues();
            }
        }
    }

    private class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private String str;
        private String namePr;

        SwingWorkerCompletionWaiter(String str, String namePr) {
            this.str = str;
            this.namePr = namePr;
        }

        SwingWorkerCompletionWaiter(String namePr) {
            this.namePr = namePr;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else {
                System.out.println("SomeThing Wrong happends with Thread Status with Name :" + str);
            }
        }
    }
}

abstract class AccurateScheduledRunnable implements Runnable {

    private ScheduledFuture<?> thisThreadsMonitor;

    public void setThreadMonitor(ScheduledFuture<?> monitor) {
        this.thisThreadsMonitor = monitor;
    }

    protected long getExecutionTime() {
        long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
        return delay;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文