How do I simulate a buffered peripheral device with SwingWorker?

一个人想着一个人 提交于 2019-11-26 11:56:11
trashgod

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.

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.
}
mKorbel

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

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;
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!